java 集合

java 集合

數據結構總覽

在這裏插入圖片描述

Collection

Collection 接口主要關注集合的添加,刪除,包含

  • isEmpty: 判斷是否沒有元素
  • size: 獲取元素個數
  • add: 添加元素
  • addAll: 添加給定集合中的所有元素,相當於並集
  • remove: 刪除元素
  • removeAll: 刪除給定集合中的所有元素,相當於差集
  • removeIf: 刪除滿足謂詞的元素
  • retainAll: 保留給定集合中的元素,相當於交集
  • contains: 判斷某個元素是否在集合內
  • containsAll: 判斷給定集合中的所有元素是否都在集合內
  • clear: 清空所有元素
  • stream: 支持流處理
{
    Collection<Integer> c = new ArrayList<>(List.of(1, 2, 3, 4, 5));
    assertEquals(c.size(), 5);
    assertFalse(c.isEmpty());
    assertTrue(c.contains(3));
    assertTrue(c.containsAll(List.of(2, 4)));
    c.clear();
    assertEquals(c.size(), 0);
    assertTrue(c.isEmpty());
}
{
    Collection<Integer> c = new ArrayList<>(List.of(1, 2, 3, 4, 5));
    c.add(6);
    assertThat(c, equalTo(List.of(1, 2, 3, 4, 5, 6)));
    c.addAll(List.of(7, 8, 9));
    assertThat(c, equalTo(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9)));
}
{
    Collection<Integer> c = new ArrayList<>(List.of(1, 2, 3, 4, 5));
    c.remove(3);
    assertThat(c, equalTo(List.of(1, 2, 4, 5)));
    c.removeAll(List.of(2, 3));
    assertThat(c, equalTo(List.of(1, 4, 5)));
    c.retainAll(List.of(1, 2, 3, 4));
    assertThat(c, equalTo(List.of(1, 4)));
    c.removeIf(x -> x % 2 == 0);
    assertThat(c, equalTo(List.of(1)));
}
{
    Collection<Integer> c = new ArrayList<>(List.of(1, 2, 3, 4, 5));
    c.forEach(System.out::print);
    assertEquals(c.stream().map(x -> x * x).mapToInt(x -> x).sum(), 55);

    for (Integer i : c) {
        System.out.print(i);
    }
}

List

List 接口爲順序表,繼承自 Collection,關注集合的定位,查找,修改和排序,底層有兩種實現,鏈表和數組,鏈表有較好的頭部插入性能,數組在隨機訪問的時候有很大優勢,util 裏主要提供了三種順序表:

  • LinkedList: 雙鏈表實現,定位元素需要遍歷,get 性能是 O(n);插入性能 O(1),但指定下標插入需要先定位;查找也需要遍歷,性能 O(n)
  • ArrayList: 數組實現,插入時需要移動數組中的元素,插入性能是 O(n),向後插入是 O(1),插入時如果數組空間不夠,需要重新申請新的空間,並將原來的元素添加到新的數組中;可以根據下標定位元素,支持隨機訪問,get 性能是 O(1);查找需要遍歷,性能 O(n)
  • Vector: 和 ArrayList 底層一樣,但是是線程安全的

ListCollection 的基礎上,提供了下面接口:

  • get: 按下標定位元素
  • indexOf: 查找元素,返回下標
  • lastIndexOf: 從後向前查找元素
  • subList: 子鏈表
  • set: 指定下標修改
  • sort: 排序
  • replaceAll: 對所有元素用 UnaryOperator 的返回值替換
{
    List<Integer> l = new ArrayList<>(List.of(1, 2, 3, 4, 5, 4, 3, 2, 1));
    assertEquals(l.get(2), Integer.valueOf(3));
    assertEquals(l.indexOf(3), 2);
    assertEquals(l.indexOf(6), -1);
    assertEquals(l.lastIndexOf(3), 6);
    assertEquals(l.subList(2, 6), List.of(3, 4, 5, 4));
}
{
    List<Integer> l = new ArrayList<>(List.of(1, 2, 3, 4, 5, 4, 3, 2, 1));
    l.set(5, 6);
    assertThat(l, equalTo(List.of(1, 2, 3, 4, 5, 6, 3, 2, 1)));
}
{
    List<Integer> l = new ArrayList<>(List.of(1, 2, 3, 4, 5, 4, 3, 2, 1));
    l.sort(Integer::compareTo);
    assertThat(l, equalTo(List.of(1, 1, 2, 2, 3, 3, 4, 4, 5)));
}
{
    List<Integer> l = new ArrayList<>(List.of(1, 2, 3, 4, 5, 4, 3, 2, 1));
    l.replaceAll(x -> x * x);
    assertThat(l, equalTo(List.of(1, 4, 9, 16, 25, 16, 9, 4, 1)));
}

Set

SetList 的本質區別在於可重複性,Set 中的元素是不可重複的,Set 又分爲有序 Set 和無序 Set,有序 Set 中的元素是按順序排列的,util 中提供了三種實現

  • TreeSet: 有序 Set,元素必須是可比較的,使用紅黑樹實現,插入刪除查找代價都是 O(lgn)
  • HashSet: 無序 Set,元素必須是能被 hash 的,使用 hash 表實現,插入刪除查找代價都是 O(1)
  • LinkedHashSet: 無序的 Set,但是提供能插入順序的遍歷,使用 hash + 鏈表實現,插入刪除查找都是 O(1)

Set 沒有提供 Collection 接口之外的接口

同時 TreeSet 還實現了 SortedSetNavigableSet

SortedSet 繼承自 Set,提供瞭如下接口:

  • first: 最小的元素
  • last: 最大的元素
  • headSet: 頭部集合,小於給定元素的元素構成的集合
  • tailSet: 尾部集合,大於等於給定元素的元素構成的集合
  • subSet: 子集,[from, to) 集合
SortedSet<String> set = IntStream.range(0, 10).boxed().map(x -> "key" + x).collect(Collectors.toCollection(TreeSet::new));
assertEquals(set.first(), "key0");
assertEquals(set.last(), "key9");
assertThat(set.headSet("key3"), equalTo(Set.of("key0", "key1", "key2")));
assertThat(set.tailSet("key7"), equalTo(Set.of("key7", "key8", "key9")));
assertThat(set.subSet("key3", "key7"), equalTo(Set.of("key3", "key4", "key5", "key6")));

NavigableSet 繼承自 SortedSet,提供瞭如下接口:

  • lower: 小於給定值的最大值
  • higher: 大於給定值的最小值
  • floor: 小於等於給定值中的最大值
  • ceiling: 大於等於給定值的最小值
  • pollFirst: 刪除並獲取最小值
  • pollLast: 刪除並獲取最大值
  • descendingSet: 獲取倒排的集合
  • headSet: 頭部集合,提供額外參數是否包含給定值
  • tailSet: 尾部集合,提供額外參數是否包含給定值
  • subSet: 子集,提供額外參數是否包含給定值
{
    NavigableSet<String> set = IntStream.range(0, 10).boxed().map(x -> "key" + x).collect(Collectors.toCollection(TreeSet::new));
    assertEquals(set.lower("key6"), "key5");    // <
    assertEquals(set.higher("key6"), "key7");   // >
    assertEquals(set.floor("key6"), "key6");    // <=
    assertEquals(set.ceiling("key6"), "key6");  // >=
    set.remove("key6");
    assertEquals(set.floor("key6"), "key5");
    assertEquals(set.ceiling("key6"), "key7");
}
{
    NavigableSet<String> set = IntStream.range(0, 5).boxed().map(x -> "key" + x).collect(Collectors.toCollection(TreeSet::new));
    assertEquals(set.pollFirst(), "key0");
    assertThat(set, equalTo(Set.of("key1", "key2", "key3", "key4")));
    assertEquals(set.pollLast(), "key4");
    assertThat(set, equalTo(Set.of("key1", "key2", "key3")));
}
{
    NavigableSet<String> set = IntStream.range(0, 10).boxed().map(x -> "key" + x).collect(Collectors.toCollection(TreeSet::new));
    assertThat(set.descendingSet(), equalTo(Set.of("key9", "key8", "key7", "key6", "key5", "key4", "key3", "key2", "key1", "key0")));
    assertThat(set.headSet("key3", false), equalTo(Set.of("key0", "key1", "key2")));
    assertThat(set.tailSet("key7", true), equalTo(Set.of("key7", "key8", "key9")));
    assertThat(set.subSet("key3", true, "key7", false), equalTo(Set.of("key3", "key4", "key5", "key6")));
}

Queue

Queue 隊列(先進先出),繼承自 Collection,關注集合的有序性,支持尾部插入,頭部刪除,以及頭部元素的獲取,util 提供了三種 Queue

  • LinkedList: LinkedList 實現了 Queue 的接口,元素按插入順序排列
  • ArrayDeque: 數組實現的 Queue,元素按插入順序排列
  • PriorityQueue: 優先隊列,堆實現,元素按從小到大排列

QueueCollection 基礎上提供瞭如下接口:

  • add: 添加元素,如果隊列滿了,拋出異常
  • remove: 刪除元素,如果隊列爲空,拋出異常
  • element: 獲取頭部元素,如果隊列爲空,拋出異常
  • offer: 添加元素,如果隊列滿了,返回 false
  • poll: 刪除元素,如果隊列爲空,返回 null
  • peek: 獲取頭部元素,如果隊列爲空,返回 null
{
    Queue<Integer> queue = new LinkedList<>();
    // add / remove / element
    assertThrows(NoSuchElementException.class, queue::remove);
    assertThrows(NoSuchElementException.class, queue::element);
    IntStream.range(0, 10).forEach(queue::add);
    assertThat(queue.toArray(), equalTo(new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
    assertEquals(queue.element(), Integer.valueOf(0));
    assertEquals(queue.remove(), Integer.valueOf(0));
}
{
    Queue<Integer> queue = new LinkedList<>();
    // offer / poll / peek
    assertEquals(queue.poll(), null);
    assertEquals(queue.peek(), null);
    IntStream.range(0, 10).forEach(queue::offer);
    assertThat(queue.toArray(), equalTo(new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
    assertEquals(queue.peek(), Integer.valueOf(0));
    assertEquals(queue.poll(), Integer.valueOf(0));
}

Deque

Deque 雙端隊列,繼承自 Queue,關注集合的兩端的插入和刪除以及兩端元素的獲取,util 提供了兩種 Deque

  • LinkedList: 鏈表實現的 Deque
  • ArrayDeque: 數組實現的 Deque

DequeQueue 的基礎上提供了下面接口:

  • addFirst: 頭部插入,隊列滿,拋異常
  • addLast: 尾部插入,隊列滿,拋異常
  • removeFirst: 頭部刪除,隊列空,拋異常
  • removeLast: 尾部刪除,隊列空,拋異常
  • getFirst: 獲取頭部元素,隊列空,拋異常
  • getLast: 獲取尾部元素,隊列空,拋異常
  • offerFirst: 頭部插入,隊列滿,返回 false
  • offerLast: 尾部插入,隊列滿,返回 false
  • pollFirst: 頭部刪除,隊列空,返回 null
  • pollLast: 尾部刪除,隊列空,返回 null
  • peekFirst: 獲取頭部,隊列空,返回 null
  • peekLast: 獲取尾部,隊列空,返回 null
  • push: 作爲 Stack 使用,插入元素(頭部插入),隊列滿,拋異常
  • pop: 作爲 Stack 使用,刪除元素(頭部刪除),隊列空,拋異常
  • removeFirstOccurrence: 刪除第一個與給定值相等的對象,返回是否有元素刪除
  • removeLastOccurrence: 刪除最後一個與給定值相等的對象,返回是否有元素刪除
{
    Deque<Integer> deque = new ArrayDeque<>();
    assertThrows(NoSuchElementException.class, deque::getFirst);
    assertThrows(NoSuchElementException.class, deque::getLast);
    assertThrows(NoSuchElementException.class, deque::removeFirst);
    assertThrows(NoSuchElementException.class, deque::removeLast);
    IntStream.range(0, 5).forEach(deque::addFirst);
    IntStream.range(5, 10).forEach(deque::addLast);
    assertThat(deque.toArray(), equalTo(new Integer[]{4, 3, 2, 1, 0, 5, 6, 7, 8, 9}));
    assertEquals(deque.getFirst(), Integer.valueOf(4));
    assertEquals(deque.getLast(), Integer.valueOf(9));
    assertEquals(deque.removeFirst(), Integer.valueOf(4));
    assertEquals(deque.removeLast(), Integer.valueOf(9));
}
{
    Deque<Integer> deque = new ArrayDeque<>();
    assertEquals(deque.peekFirst(), null);
    assertEquals(deque.peekLast(), null);
    assertEquals(deque.pollFirst(), null);
    assertEquals(deque.pollLast(), null);
    IntStream.range(0, 5).forEach(deque::offerFirst);
    IntStream.range(5, 10).forEach(deque::offerLast);
    assertThat(deque.toArray(), equalTo(new Integer[]{4, 3, 2, 1, 0, 5, 6, 7, 8, 9}));
    assertEquals(deque.peekFirst(), Integer.valueOf(4));
    assertEquals(deque.peekLast(), Integer.valueOf(9));
    assertEquals(deque.pollFirst(), Integer.valueOf(4));
    assertEquals(deque.pollLast(), Integer.valueOf(9));
}
{
    Deque<Integer> deque = new ArrayDeque<>();
    IntStream.range(0, 10).forEach(deque::push);
    assertThat(deque.toArray(), equalTo(new Integer[]{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}));
    assertEquals(deque.element(), Integer.valueOf(9));
    assertEquals(deque.pop(), Integer.valueOf(9));
}
{
    Deque<Integer> deque = new ArrayDeque<>();
    IntStream.range(0, 10).forEach(deque::push);
    assertTrue(deque.removeFirstOccurrence(2));
    assertTrue(deque.removeLastOccurrence(8));
    assertThat(deque.toArray(), equalTo(new Integer[]{9, 7, 6, 5, 4, 3, 1, 0}));
}

Stack

Queue先進先出不同,Stack 是一種代表後進先出的數據結構,util 中並沒有提供 Stack 接口,事實上 Deque 中已經包含了 Stack 接口,因此當你需要一個 Stack 的時候,可以構造一個 Deque,java doc 也是這麼建議的

此外,util 中還有一個 Stack 類,繼承自 Vector,線程安全,這個類的設計和定位比較尷尬,不建議使用

鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章