Java集合類初探

LifeOfCoding發表於2020-07-11

目錄

概述

  Java中基本的常用的集合類,主要包含:

  • List
  • Set
  • Queue
  • Map

這幾種型別的繼承關係如圖: 圖片引自——Collection 和 Map的繼承體系

其中 List、Queue 和 Set 繼承自 Collection 介面,這三種集合的結構都比較簡單,都是普通的元素的集合,而 Map 相對複雜一點,是鍵值對(key-value)的結構。

Iterable和Iterator


Iterable

  Iterable 介面是 Collection 介面的“超類”,也就是說 Iterable 介面是 List、Set、Queue 這類簡單集合的頂級介面,看下 Iterable 介面有些啥:

Iterable 介面包含三個方法:

  • iterator() 返回一個迭代器用於迭代、遍歷集合;
  • forEach(Consumer<? super T>) 增強型for迴圈,配合 lambda 表示式食用更香;
  • spliterator() 同樣返回一個迭代器,該迭代器是“可分割迭代器”,為並行遍歷集合而設計。

Iterable 介面的 forEach() 和 spliterator() 方法是用 default 關鍵字修飾的,介面裡包含了方法的預設實現。

Iterator

  Iterator 也是一個介面,用於集合的遍歷:

  • hasNext() 是否還有下一個元素,用於控制集合的遍歷的結束;
  • next() 返回一個元素,遊標往後移。
  • remove() 去除當前的元素(最近一次呼叫 next() 返回的元素)。想要在遍歷集合時刪除元素,需要用到 Iterator 迭代器和該方法。
  • forEachRemaining(Consumer<? super E>) 對剩下的元素(即尚未被遍歷到的元素)進行某些操作。和 Iterable 介面的 forEach() 有點類似。

Collection


  Collection 介面包含了集合的基本的增刪查操作。既然是集合,那麼以下基本方法是非常必要的:

  • 獲取集合大小: size();
  • 新增元素: add();
  • 移除元素: remove();
  • 判斷包含: contains();
  • 判斷元素是否相等: equals();

為了增加可用性,又加多了幾個方法:

  • isEmpty() 判空,少寫幾句 "collection.size==0";

  • containsAll() 判斷是否包含另一個集合裡的所有元素;

  • toArray()、toArray(T[]) 把集合轉化為陣列;

  • clear() 清除所有元素;

  • removeIf() 條件刪除;

  • stream() 流操作;

  • removeAll() 取補集,把另一個集合也包含的元素去除;

  • addAll() 把另一個集合的元素新增進來,相當於取並集;

  • retainAll() 取交集,把另一個集合也包含的元素保留下來;

  • stream()、parrallelStream() 流操作、並行流操作。

List


  List 中文譯作“清單、列表”,List 是有序的,是按照一定的順序來儲存元素的,既然是有序的集合,就應該可以通過“遊標”來進行訪問,因此,List 在 Collection 的基礎上新增了通過遊標訪問元素,或通過元素獲取遊標的方法:

  • get(int);

  • add(int, E);

  • remove(int);

  • indexOf(Object);

  • subList(int, int);通過遊標擷取子序列。

  • lastIndexOf(Object); 因為 List 是有序的,所以可以通過“遊標”確定唯一的元素,因此 List 是允許集合中包含相同(相等)的多個元素的,lastIndexOf(Object) 可以用來在集合包含多個相同元素時,獲取該元素最後出現的位置。

除此之外,List 還加上了 Collection 沒有的“改”操作,實現了集合的“增刪查改”:

  • set(int, E); 替換某個位置的元素,既然是替換,前提條件是該位置含有元素。

因為 List 是有序集合,因此給 List 加上排序操作是可取的:

  • sort(Comparator<? super E>);

在List介面中有一個 “ListIerator()”的方法,該方法返回一個 ListIterator 物件。

ListIterator 繼承了 Iterator 介面,提供了 previous 操作,以及與遊標相關的 nextIndex() 和 previousIndex()。

Queue


  Queue(佇列)是一種特殊的線性表,有的時候我們會因為業務需求,需要一個“規定容量的Queue”,這時,如果佇列滿了卻又要新增元素,該採取怎樣的策略,或者佇列為空時呼叫remove該如何處理,這是需要考慮的。Queue 繼承了 Collection,又提供了幾個自己的方法:

  • offer(E): 隊尾新增元素,與 add 對應,當佇列已滿時返回false, add()方法這種情況下則會丟擲異常。

  • poll(): 查詢並刪除隊頭元素,與 remove 對應,當佇列為空時,不會像 remove 那樣丟擲異常,而是會返回 null;

  • element(): 查詢隊頭元素但不刪除,與 peek 對應,佇列為空時丟擲異常。

  • peek(): 查詢隊頭元素但不刪除,佇列為空時返回 null;

Set


  Set 中文是“一組、一套”的意思。與 List 相比,Set 沒有 List 的“列表、清單”的“列”的語義。Set 是無序的。 Set 與 Collection 在語義以及屬性上最為接近,Set 繼承了 Collection 的方法,沒有自己新新增的方法,如下所示:

  
Set 是沒有 List 的 Index 的概念的,List 可以用 index 唯一確定一個元素,Map 可以用 key 唯一確定一個元素,而 Set 只能靠自身來唯一確定一個元素,所以 Set 裡面的元素是不能重複的。正因為 Set 這一特性,可以使用 Set 給集合去重。

Map

  在詞典中,Map 作動詞時是“對映”的意思,key-value,key 對映到 value,可以通過 key 來獲取 value。Map 和 Iterable 一樣是頂級介面。Map 介面 UML 圖如下:

雖然 Map 是一個頂級介面,但是 Map 和 Set、List、Queue應該是同一層級的介面,它們的方法很多是類似的(功能上)。
基本的增刪查改:

  • size();
  • isEmpty();
  • containsKey();
  • containsValue();
  • get();
  • put();
  • remove();
  • putAll();
  • clear();
  • replace()

鍵和值的遍歷:

  • keySet(): 返回 key 的 Set 集合,因為 Map 靠 key 來唯一確定一個元素,所以 Map 的 key 是不能重複的,自然地,返回的 key 集合是一個 Set;

  • values(): value 的值不會影響到 Map 的結構,value 是可以重複的,也是可以為 null 的,所以返回的 value 集合應該是一個 Collection;

  • entrySet: 該方法返回的是 Entry 的 Set 集合, Entry 是 Map 的內部介面,可把其看作一對鍵值對的組合:

其它的方法:

  • getOrDefault(Object, V): 如果找到 key 為 object 的鍵值對,則返回其 value, 否則返回自己傳入的 V;

  • putIfAbsent(K, V): 與 put(K, V) 對應,如果 Map 中已經包含 key 為 K 的鍵值對或鍵值對的值為空,put(K, V) 方法會用 V 覆蓋原來的值;而 putIfAbsent(K, V)方法僅返回現在的值,不覆蓋;當 Map 中不存在 key 為 k 的鍵值對時,put 和 putIfAbsent 都會新增新的元素。 概況說就是如果指定的鍵尚未對映(對映即關聯,就是 key 和 value 的關聯關係, value 為 null 也視為未對映或者說未關聯),將其與給定值相關聯並返回 null ,否則返回當前值。

  • compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction): 傳一個BiFunction 物件進去實現值的“計算”,此處的“計算”應該是指“操作”,不僅僅是指算術上的“計算”。這與 VueJS 的“計算屬性”有點類似。可以通過該方法實現 Map 資料的轉換、資料的統計。
    BiFunction是一個介面,關鍵方法和說明如下圖所示,可配合 lambda 表示式,是函數語言程式設計的思想。



    詳情見Java基礎之Java8中Map的compute的使用

  • computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction): 同 putIfAbsent 一樣,如果 key 未對映( value 為 null 也視為未對映),則嘗試使用給定的對映函式計算其值,並將其輸入到此對映中(即設定 value )。

  • computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction): Present 與 Absent 相反,前者是“存在”的意思,後者是“不在”的意思,顧名思義,該方法是在指定的 key 在 Map 中存在對映時,則嘗試計算給定key 及其當前值的新值。

  • merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction): merge 其實與 compute 這幾個方法的作用是一樣的,對 key 對映的 value 值進行變換,只是該方法多提供了一個引數 “V value”,相當於用這個引數的 value 和 key 所對應的 value 結合merge)起來一起變換;
      比如想要讓 Map 中儲存的學生的年齡加“1”,使用 computer 的話,可以在函式裡寫定引數“1”;使用 merge 的話,可以在呼叫 merge 方法時把“1”傳進去。這時,如果需求變了,年齡不是加“1”,而是加“2”,使用compute 方法的話需要到方法裡面改這個“1”(magic num);而使用 merge 的話只需要在方法呼叫時改引數,不用改其它程式碼。

相關文章