java集合概述

假裝在扯淡發表於2021-10-19

關係總覽

java.util包下集合類關係圖如下,不包含concurrent包,斜體標識介面,粗體標識抽象類,紅線標識繼承或實現。

Collections

Collection

Collection提供以下介面方法及預設介面方法

Collection

AbstractCollcetion

概述

該類實現類Collection介面的大部分方法,其中提供實現的具體方法,大部分依賴於待子類實現的抽象方法。

繼承自該類的子類,按可變性來說,對子類要求如下:

  • 不可修改Collection:如果要實現一個不可修改的Collection,那麼子類只需要實現iterator和size方法。iterator返回的迭代器,必須實現hasNext、next方法
  • 可修改Collection:如果要實現一個可修改的Collection,除了iterator和size方法,子類還需要覆蓋add方法;iterator返回的迭代器,必須實現remove方法。
抽象方法
  • iterator():源自Iterable介面。返回迭代器物件。
  • size():源自Collection介面。返回集合元素數量。
具體方法
  • isEmpty():判斷集合是否為空。具體實現依賴子類實現的size(),判斷size()==0
  • contains(Object):判斷集合是否包含指定元素。具體實現依賴子類實現的iterator(),透過迭代器遍歷集合中元素,若存在元素與引數物件相等(同為null,或者equals返回true),返回true;否則返回false
  • toArray():返回集合元素組成的陣列。具體實現依賴子類實現的iterator()和size(),透過size()構建對應長度的陣列,透過迭代器遍歷所有元素初始化陣列
  • toArray(T[]):返回集合元素組成的陣列。具體實現依賴子類實現的iterator()和size()。如果引數傳入的陣列長度不能容納所有元素,則新建立一個更大的陣列儲存所有元素並返回。
  • add(E) : 預設丟擲UnsupportedOperationException異常
  • remove(Object):刪除指定元素。具體實現依賴子類實現的iterator()。透過迭代器遍歷集合中元素,若存在元素與引數物件相等(同為null,或者equals返回true),則呼叫迭代器的remove(),同時返回true;否則返回false
  • containsAll(Collection<?> ):如果引數集合是當前集合的子集,則返回true,否則返回false。具體實現是for迴圈遍歷引數集合中的元素,呼叫contains(Object),如果有一個元素不包含在當前集合,則返回false;如果均包含在當前集合,返回true
  • addAll(Collection<?> ):將引數集合中的所有元素加入到當前集合。具體實現依賴add(E) 。具體實現是for迴圈遍歷引數集合中元素,逐個呼叫add(E),只要有一個新增成功,則最終返回true
  • removeAll(Collection<?> ):刪除當前集合中與引數集合中的元素相等的元素。具體實現依賴iterator()。透過迭代器遍歷當前集合中元素,若引數集合包含該元素,則呼叫迭代器的remove()進行刪除,若成功刪除過一個元素,則最終返回true。否則返回false。
  • retainAll(Collection<?> ):刪除當前集合中與引數集合中的元素不相等的元素。具體實現依賴iterator()。透過迭代器遍歷當前集合中元素,若引數集合不包含該元素,則呼叫迭代器的remove()進行刪除,若成功刪除過一個元素,則最終返回true。否則返回false。
  • clear():刪除集合所有元素。具體實現依賴iterator()。透過迭代器遍歷當前集合中元素,呼叫迭代器的remove()進行刪除
  • toString():列印出集合中所有元素,元素中間用逗號分隔,兩邊用中括號。具體實現依賴iterator()。透過迭代器遍歷當前集合中的元素,實用StringBuilder進行拼接,最終返回所有元素組成的字串。例如 [1,2,3,4,5]

Set

Set 介面覆蓋了Collection介面的預設方法spliterator,指定characteristics為DISTINCT;除此之外,沒有新增任何新方法。

Set

AbstractSet

概述

AbstractSet繼承AbstractCollcetion,實現Set介面。AbstractSet規定不允許子類新增相同元素到集合中,但具體邏輯中沒有做相應控制,需要子類具體遵循該規定進行實現。AbstractSet覆蓋了AbstractCollection的removeAll方法,同時實現了equals和hashCode方法,除此之外沒有定義任何額外的抽象方法。

抽象方法
  • iterator():源自Iterable介面。返回迭代器物件。
  • size():源自Collection介面。返回集合元素數量。
具體方法
  • equals(Object):判斷是否相等;若指向同一物件則返回true;若引數不是Set的例項則返回false;若引數是Set的例項,則比較引數Set和當前Set的size(),如果不相等則返回false;如果相等,則呼叫父類實現的containsAll(Collection)返回結果。
  • hashCode():計算hash值;該方法依賴於iterator()。透過迭代器遍歷集合中元素,將所有元素的hashCode累加並返回。
  • removeAll(Collection):刪除當前集合中與引數集合中的元素相等的元素。該方法覆蓋了AbstractCollection的實現。透過size()和iterator()獲取元素較少的集合的迭代器,然後迭代出所有元素,如果呼叫較大集合的contains(E),如果包含則刪除。如果刪除過一個元素,則最終返回true

List

List介面覆蓋了Collection介面的預設方法spliterator,指定characteristics為ORDERED;除此之外,新增了圖中所圈的方法。

List

List介面新增方法簡述:

  • 位置訪問操作(Postional Access Operations)

    • get(int): 按index查詢元素
    • set(int,E): 按index修改元素
    • add(int, E): 按index增加元素
    • remove(int): 按index刪除元素
  • 搜尋操作(Search Operations)

    判斷相等的條件: o==null?get(i)==null:o.equals(get(i))

    • indexOf(Object): 返回列表中第一個等於引數元素的下標
    • lastIndexOf(Object): 返回列表中最後一個等於引數元素的下標
  • 列表迭代器(List Iterators)

    ListIterator是List介面提供特殊的Iterator,允許元素插入、替換操作,除了Iterator常規的操作外還可以支援雙向訪問。

    • listIterator():返回ListIterator物件,下標從第一個元素開始
    • listIterator(int):返回ListIterator物件,下標從指定元素位置開始
  • 檢視(View)

    • subList(int,int):返回list中指定部分的檢視,包含fromIndex,不包含toIndex,如果二者相等,則返回空。

AbstractList

概述

AbstractList繼承抽象類AbstractCollection,實現了List介面中的大部分方法,支援隨機訪問(random access),對於需要順序訪問(sequential access)的資料,優先用子類抽象類AbstractSequentialList的例項

  • 子類按可變性分類

    • 不可修改list:如果要實現一個不可修改的list,那麼子類只需要實現get和size方法;
    • 可修改list:如果要實現一個可修改的的list,除了get和size方法,子類還需要覆蓋set方法;如果可改變的list是變長的,還需要覆蓋add和remove方法。
  • 迭代器實現

    • Iterator

      對外提供hasNext、next、remove方法。remove方法不是執行緒安全的,但提供了基本的fast-fail功能,儘管多執行緒下存在一定缺陷。具體如下:

      private class Itr implements Iterator<E> {
              /**
               * Index of element to be returned by subsequent call to next.
               */
              int cursor = 0;
      
              /**
               * Index of element returned by most recent call to next or
               * previous.  Reset to -1 if this element is deleted by a call
               * to remove.
               */
              int lastRet = -1;
      
              /**
               * The modCount value that the iterator believes that the backing
               * List should have.  If this expectation is violated, the iterator
               * has detected concurrent modification.
               */
              int expectedModCount = modCount;
      
              public boolean hasNext() {
                  return cursor != size();
              }
      
              public E next() {
                  checkForComodification();
                  try {
                      int i = cursor;
                      E next = get(i);
                      lastRet = i;
                      cursor = i + 1;
                      return next;
                  } catch (IndexOutOfBoundsException e) {
                      checkForComodification();
                      throw new NoSuchElementException();
                  }
              }
      
              public void remove() {
                  if (lastRet < 0)
                      throw new IllegalStateException();
                  checkForComodification();
      
                  try {
                      AbstractList.this.remove(lastRet);
                      if (lastRet < cursor)
                          cursor--;
                      lastRet = -1;
                      expectedModCount = modCount;
                  } catch (IndexOutOfBoundsException e) {
                      throw new ConcurrentModificationException();
                  }
              }
      
              final void checkForComodification() {
                  if (modCount != expectedModCount)
                      throw new ConcurrentModificationException();
              }
          }

      AbstractList 透過內部成員變數 modCount 來記錄list的長度發生變化的次數,例如add和remove方法會改變list的長度,modCount 的值會隨之增加。

      Itr 透過成員變數expectedModCount來記錄list當前的modCount值,呼叫next和remove方法時,首先會檢查迭代器中的儲存的expectedModCount是否等於當前list的modCount值:

      • 若不等於,說明迭代器再操作list的過程中,list的長度發生了變化,併發修改list長度可能會引起資料的不一致,丟擲ConcurrentModificationException
      • 若等於,則繼續執行後續邏輯;但expectedModCount等於當前list的modCount未必就代表沒有併發問題。例如,當程式執行到 AbstractList.this.remove(lastRet); 時,CPU時間片到,切換到其它執行緒執行類remove或add操作,然後切換回當前執行緒的時候 執行了 expectedModCount = modCount; 此時並沒有做到快速失敗。ListItr中的set和add方法存在類似問題。

      思考:為什麼 Itr 不在迭代器中儲存list的size,透過比較迭代器中的size和外部size是否相等來進行fast-fail ?

      回答:因為存在ABA問題。相連的remove和add操作會導致最終size相等,但是list的長度已發生改變。

    • ListIterator

      ListIterator繼承自Iterator介面,提供add、set方法用於元素的插入、替換,透過hasPrevious、previous、nextIndex、previousIndex支援雙向訪問。

抽象方法
  • get(int) : 源自List介面。根據下標獲取元素
  • size():源自Collection介面。獲取list元素數量
具體方法
  • add(E) : 呼叫add(size(), e)實現往list尾部新增元素;
  • add(int,E) : 預設丟擲UnsupportedOperationException異常
  • addAll(int,Collection):從指定下標開始,呼叫add(int,E)將引數集合的元素逐個加入list
  • set(int,E) : 預設丟擲UnsupportedOperationException異常
  • remove(int): 預設丟擲UnsupportedOperationException異常
  • indexOf(Object):使用迭代器listIterator,從list頭部查詢第一個相等元素的下標,object可為null
  • lastIndexOf(Object):使用迭代器listIterator,從list尾部查詢最後一個相等元素的下標,object可為null
  • clear():呼叫removeRange(0,size())刪除所有元素
  • iterator():返回內部私有類Itr的例項
  • listIterator(): 返回內部私有類ListItr的例項,預設從list首部開始遍歷
  • listIterator(int):返回內部私有類ListItr的例項,引數指定了從list的開始遍歷的位置
  • subList(int,int):返回SubList類的例項,該例項內部儲存了list的例項,用於檢視或操作list的指定下標區間的元素
  • equals(Object):判斷是否相等;若指向同一物件則返回true;若引數不是List的例項則返回false;若引數是list,則順序比對兩個list中對應位置的元素,如有一個equal為false則返回false,否則為true
  • hashCode():計算方式如下

        /**
         * Returns the hash code value for this list.
         *
         * <p>This implementation uses exactly the code that is used to define the
         * list hash function in the documentation for the {@link List#hashCode}
         * method.
         *
         * @return the hash code value for this list
         */
        public int hashCode() {
            int hashCode = 1;
            for (E e : this)
                hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
            return hashCode;
        }
  • removeRange(int,int):protected方法;刪除指定區間內的元素

AbstractSequentialList

概述

該類支援順序訪問(sequential access),對於隨機訪問(random access)的資料,使用父類AbstractList儲存。

所有子類都必須實現ListIterator方法和size方法,按可變性來說,對子類要求如下:

  • 不可修改list:如果要實現一個不可修改的list,那麼子類需要實現ListIterator中的hasNext、next、hasPrevious、previous、index方法;
  • 可修改list:如果要實現一個可修改的list,除不可修改中描述的方法外,還必須實現ListIterator中的set方法;如果list是變長的,還需要實現ListIterator中的add和remove方法。
抽象方法
  • listIterator(int):源自List介面。獲取迭代器物件。此類的ListIterator實現與父類AbstractList中的ListIterator實現不同:

    • AbstractList提供了迭代器ListIterator的具體實現類ListItr,AbstractList的listIterator方法不是抽象方法;而AbstractSequentialList不提供迭代器ListIterator的具體實現,需要子類必須實現
    • AbstractList提供的迭代器ListIterator預設實現ListItr中的next、set、add、remove方法依賴於AbstractList類的get、set、add、remove方法;
    • AbstractSequentialList中的提供的get、set、add、remove方法實現,依賴於ListIterator的next、set、add、remove方法
  • size():源自Collection介面。獲取list元素數量
具體方法
  • get(int) : 根據指定下標獲取元素。該方法呼叫listIterator(int).next()實現
  • add(int,E) :在指定下標後,新增元素。該方法呼叫listIterator(int).add(E)實現
  • addAll(int,Collection):從指定下標後,新增集合元素。呼叫listIterator(int)獲取迭代器,然後遍歷引數集合,逐個呼叫迭代器的add(E)方法新增集合元素
  • set(int,E) : 替換指定位置的元素。該方法呼叫listIterator(int).next()獲取老值,呼叫listIterator(int).set(E)來實現替換
  • remove(int): 刪除指定位置的元素。該方法呼叫listIterator(int).next()獲取老值,呼叫listIterator(int).remove()來實現刪除

Queue

jdk1.5新增此介面,繼承Collection介面的所有方法,另外新增下圖中所圈的方法。

Queue

各個實現類的實現原理和用途

add(E): 在佇列尾部新增元素。如果增加成功就返回true;如果當前沒有空間導致增加失敗,丟擲 IllegalStateException異常

offer(E): 在佇列尾部新增元素。如果增加成功就返回true; 如果當前沒有空間導致增加失敗,返回false;

remove(): 獲取並刪除佇列頭部元素。如果佇列為空,則丟擲NoSuchElementException異常

poll(): 獲取並刪除佇列頭部元素。如果佇列為空,則返回null;

element(): 獲取但不刪除佇列頭部元素。如果佇列為空,則丟擲NoSuchElementException異常

peek(): 獲取但不刪除佇列頭部元素。如果佇列為空,則返回null。

AbstractQueue

概述

繼承AbstractCollection類的抽象類,實現Queue介面,覆蓋AbstractCollection的add(E)、clear()、addAll(Collection<? extends E>) 方法。

繼承該類的子類,不允許往佇列中插入null元素

沒有實現Queue介面的offer(E)、poll()、peek()方法。

抽象方法
  • offer(E):源自Queue介面。在佇列尾部新增元素。如果增加成功就返回true; 如果當前沒有空間導致增加失敗,返回false
  • poll():源自Queue介面。獲取並刪除佇列頭部元素。如果佇列為空,則返回null;
  • peek():源自Queue介面。獲取但不刪除佇列頭部元素。如果佇列為空,則返回null。
  • size():源自Collection介面。獲取佇列元素數量。
  • iterator():源自Iterable介面。獲取迭代器物件。
具體方法
  • add(E):在佇列尾部新增元素。具體實現依賴子類實現的offer(E),如果offer(E)返回false,則丟擲IllegalStateException異常
  • addAll(Collection<? extends E>) :在佇列尾部新增集合。具體實現是遍歷傳入的集合,呼叫add(E)新增集合中的元素,新增完畢後,如果有一個新增成功就返回true。
  • remove():獲取並刪除佇列頭部元素。具體實現依賴子類實現的poll(),如果poll()返回null,則丟擲NoSuchElementException異常
  • element():獲取但不刪除佇列頭部元素。具體實現依賴子類實現的peek(),如果peek()返回null,則丟擲NoSuchElementException異常
  • clear():刪除佇列中的所有元素。具體實現是迴圈呼叫poll(),直到poll()返回null

Deque

  • 雙端佇列,Deque來源於double ended queue的縮寫,發音“deck”。
  • 不同於List,Deque不支援隨機訪問;
  • 儘管Deque介面沒有禁止插入null,但是具體實現應該禁止插入null,因為Deque的方法返回null一般用來表示佇列為空。
  • jdk1.6新增此介面,繼承Queue介面的所有方法,另外新增下圖中所圈的方法。

Deque

方法說明

  • Deque介面對佇列元素提供的插入、刪除、檢查操作,對於每種操作Deque分別提供佇列頭操作、對列尾操作,對於每個這樣的操作,又分別提供丟擲異常和返回指定值的方法,具體如下:
<caption>Summary of Deque methods</caption>
First Element (Head) Last Element (Tail)
Throws exception Special value Throws exception Special value
Insert addFirst(e) offerFirst(e) addLast(e) offerLast(e)
Remove removeFirst() pollFirst() removeLast() pollLast()
Examine getFirst() peekFirst() getLast() peekLast()
  • Deque被當作佇列(先進先出 FIFO)使用時,在佇列尾部新增元素,在佇列頭部獲取元素,與Queue介面的方法有如下對應關係:
<caption>Comparison of Queue and Deque methods</caption>
Queue Method Equivalent Deque Method
add(e) addLast(e)
offer(e) offerLast(e)
remove() removeFirst()
poll() pollFirst()
element() getFirst()
peek() peekFirst()
  • Deque被當作棧(後進先出 LIFO)使用時,在棧頭部新增並獲取元素,類似於類Stack的實現,與Stack介面的方法有如下對應關係:
<caption>Comparison of Stack and Deque methods</caption>
Stack Method Equivalent Deque Method
push(e) addFirst(e) or push(e)
pop() removeFirst() or pop()
peek() peekFirst()
  • removeFirstOccurrence(Object):從對佇列頭部刪除第一個匹配元素。如果存在這樣的元素被刪除則返回true,否則返回false
  • removeLastOccurrence(Object):從佇列尾部刪除第一個匹配元素。如果存在這樣的元素被刪除則返回true,否則返回false
  • descendingIterator():返回反序的迭代器。iterator()返回的迭代器是從佇列頭到佇列尾遍歷,descendingIterator()返回的迭代器,是從佇列尾到佇列頭遍歷

Map

Map中儲存鍵值對,鍵不可以重複,每一個鍵至多對應一個值。

如果將可變物件作為鍵,必須非常訊息。當前Map不允許將自身作為鍵,但可以將自身作為值。

Map

方法說明

Map介面方法如下(不包含預設方法):

  • 查詢操作(Query Operations)

    • size():返回鍵值對的數量。
    • isEmpty():如果Map中沒有鍵值對,返回true。
    • containsKey(Object):如果Map中包含指定Key,返回true。比較Key相等條件:key==null ? k==null : key.equals(k)
    • containsValue(Object):如果Map中包含一個或更多key對應指定Value,返回true。比較Value相等條件:value==null ? v==null : value.equals(v),對於大多數實現來說,這個操作可能需要基於size()的線性時間
    • get(Object):返回引數Key對應的Value,如果當前集合不存在則返回null。如果引數Key為null,而具體實現不允許Key為null,則丟擲NullPointerException
  • 修改操作(Modification Operations)

    • put(K , V ):將指定Key與指定Value相關聯,如果當前Map中不存在該Key則插入,如果存在該Key,則替換對應的Value。如果引數Key為null,而具體實現不允許Key為null,則丟擲NullPointerException
    • remove(Object):刪除當前Map中指定Key的鍵值對。
  • 批次操作(Bulk Operations)

    • putAll(Map<? extends K, ? extends V>):將引數Map中的鍵值對,複製到當前Map。
    • clear():刪除所有鍵值對。
  • 檢視(Views)

    • keySet():返回當前Map所有Key組成的Set。Map中Key的改變會影響到該Set,反之亦然。返回的Set支援remove操作,但不支援add或addAll操作。刪除Set中的某個元素,會刪除Map中該元素對應的鍵值對。
    • values():返回當前Map所有Value組成的集合。Map中Value的改變會影響到該集合,反之亦然。返回的集合支援remove操作,但不支援add或addAll操作。刪除集合中的某個元素,會刪除Map中該元素對應的鍵值對。
    • entrySet():返回當前Map所有Entry組成的Set。Map中Entry的改變會影響到該Set,反之亦然。返回的Set支援remove操作,但不支援add或addAll操作。刪除Set中的某個元素,會刪除Map中該元素對應的鍵值對。
  • 比較和雜湊(Comparison and hashing)

    • equals(Object):如果兩個Map相等則返回true。判斷兩個Map物件m1和m2相等的依據是m1.entrySet().equals(m2.entrySet()),這樣的實現保證了即便是m1和m2的有具體不同的實現類,equels方法也可以比較。
    • hashCode():返回當前Map的雜湊值。當前Map的雜湊值,是entrySet()中元素的雜湊值之和。

Entry

Entry是Map介面的內建介面,每對鍵值對應一個Entry物件。Map的entrySet()返回包含Entry物件的Set。透過Entry可以直接修改Map。

方法說明(不含預設方法)
  • getKey():返回當前Entry對應的Key。
  • getValue():返回當前Entry對應的Value。
  • setValue():替換當前Entry對應的Value。
  • equals(Object):比較當前Entry與引數物件是否相等。如果引數物件也是Entry,比較方式:(e1.getKey()==null ? e2.getKey()==null : e1.getKey().equals(e2.getKey())) && (e1.getValue()==null ? e2.getValue()==null : e1.getValue().equals(e2.getValue()))
  • hashCode():返回當前Entry的雜湊值。計算方式:(e.getKey()==null ? 0 : e.getKey().hashCode()) ^(e.getValue()==null ? 0 : e.getValue().hashCode())

AbstractMap

概述

抽象類AbstractMap實現Map介面,所有具體方法均依賴抽象方法entrySet()。

抽象方法
  • entrySet:返回當前Map所有Entry組成的Set。Map中Entry的改變會影響到該Set,反之亦然。返回的Set支援remove操作,但不支援add或addAll操作。刪除Set中的某個元素,會刪除Map中該元素對應的鍵值對。
具體方法

所有具體方法均依賴entrySet的實現。

  • keySet():返回當前Map所有Key組成的Set。Map中Key的改變會影響到該Set,反之亦然。返回的Set支援remove操作,但不支援add或addAll操作。刪除Set中的某個元素,會刪除Map中該元素對應的鍵值對。具體實現:內部構造一個AbstractSet物件,AbstractSet實現的新建立的iterator物件,實際操作的是entrySet返回的迭代器,具體程式碼如下:

        public Set<K> keySet() {
            Set<K> ks = keySet;
            if (ks == null) {
                ks = new AbstractSet<K>() {
                    public Iterator<K> iterator() {
                        return new Iterator<K>() {
                            private Iterator<Entry<K,V>> i = entrySet().iterator();
    
                            public boolean hasNext() {
                                return i.hasNext();
                            }
    
                            public K next() {
                                return i.next().getKey();
                            }
    
                            public void remove() {
                                i.remove();
                            }
                        };
                    }
    
                    public int size() {
                        return AbstractMap.this.size();
                    }
    
                    public boolean isEmpty() {
                        return AbstractMap.this.isEmpty();
                    }
    
                    public void clear() {
                        AbstractMap.this.clear();
                    }
    
                    public boolean contains(Object k) {
                        return AbstractMap.this.containsKey(k);
                    }
                };
                keySet = ks;
            }
            return ks;
        }
  • values():返回當前Map所有Value組成的集合。Map中Value的改變會影響到該集合,反之亦然。返回的集合支援remove操作,但不支援add或addAll操作。刪除集合中的某個元素,會刪除Map中該元素對應的鍵值對。具體實現:內部構造一個AbstractCollection物件,AbstractCollection實現的新建立的iterator物件,實際操作的是entrySet返回的迭代器,具體程式碼如下:

        public Collection<V> values() {
            Collection<V> vals = values;
            if (vals == null) {
                vals = new AbstractCollection<V>() {
                    public Iterator<V> iterator() {
                        return new Iterator<V>() {
                            private Iterator<Entry<K,V>> i = entrySet().iterator();
    
                            public boolean hasNext() {
                                return i.hasNext();
                            }
    
                            public V next() {
                                return i.next().getValue();
                            }
    
                            public void remove() {
                                i.remove();
                            }
                        };
                    }
    
                    public int size() {
                        return AbstractMap.this.size();
                    }
    
                    public boolean isEmpty() {
                        return AbstractMap.this.isEmpty();
                    }
    
                    public void clear() {
                        AbstractMap.this.clear();
                    }
    
                    public boolean contains(Object v) {
                        return AbstractMap.this.containsValue(v);
                    }
                };
                values = vals;
            }
            return vals;
        }

相關文章