為什麼要使用集合
當我們需要儲存一組型別相同的資料的時候,我們應該是用一個容器來儲存,這個容器就是陣列,但是,使用陣列儲存物件具有一定的弊端, 因為我們在實際開發中,儲存的資料的型別是多種多樣的,於是,就出現了“集合”,集合同樣也是用來儲存多個資料的。
陣列的缺點是一旦宣告之後,長度就不可變了;同時,宣告陣列時的資料型別也決定了該陣列儲存的資料的型別;而且,陣列儲存的資料是有序的、可重複的,特點單一。 但是集合提高了資料儲存的靈活性,Java 集合不僅可以用來儲存不同型別不同數量的物件,還可以儲存具有對映關係的資料。
Java 容器概述
從下圖可以看出,在 Java 中除了以 Map
結尾的類之外, 其他類都實現了 Collection
介面。
並且,以 Map
結尾的類都實現了 Map
介面。
如何選用集合
主要根據集合的特點來選用,比如我們需要根據鍵值獲取到元素值時就選用 Map
介面下的集合,需要排序時選擇 TreeMap
,不需要排序時就選擇 HashMap
,需要保證執行緒安全就選用 ConcurrentHashMap
。
當我們只需要存放元素值時,就選擇實現Collection
介面的集合,需要保證元素唯一時選擇實現 Set
介面的集合比如 TreeSet
或 HashSet
,不需要就選擇實現 List
介面的比如 ArrayList
或 LinkedList
,然後再根據實現這些介面的集合的特點來選用。
List、Set、Map 三者的區別
List
(對付順序的好幫手): 儲存的元素是有序的、可重複的。Set
(注重獨一無二的性質): 儲存的元素是無序的、不可重複的。Map
(用 Key 來搜尋的專家): 使用鍵值對(kye-value)儲存,類似於數學上的函式 y=f(x),“x”代表 key,"y"代表 value,Key 是無序的、不可重複的,value 是無序的、可重複的,每個鍵最多對映到一個值。
底層資料結構
先來看一下 Collection
介面下面的集合。
1. List
Arraylist
:Object[]
陣列Vector
:Object[]
陣列LinkedList
: 雙向連結串列(JDK1.6 之前為迴圈連結串列,JDK1.7 取消了迴圈)
2. Set
HashSet
(無序,唯一): 基於HashMap
實現的,底層採用HashMap
來儲存元素LinkedHashSet
:LinkedHashSet
是HashSet
的子類,並且其內部是通過LinkedHashMap
來實現的。有點類似於我們之前說的LinkedHashMap
其內部是基於HashMap
實現一樣,不過還是有一點點區別的TreeSet
(有序,唯一): 紅黑樹(自平衡的排序二叉樹)
3. Map
再來看看 Map
介面下面的集合。
HashMap
: JDK1.8 之前HashMap
由陣列+連結串列組成的,陣列是HashMap
的主體,連結串列則是主要為了解決雜湊衝突而存在的(“拉鍊法”解決衝突)。JDK1.8 以後在解決雜湊衝突時有了較大的變化,當連結串列長度大於閾值(預設為 8)(將連結串列轉換成紅黑樹前會判斷,如果當前陣列的長度小於 64,那麼會選擇先進行陣列擴容,而不是轉換為紅黑樹)時,將連結串列轉化為紅黑樹,以減少搜尋時間LinkedHashMap
:LinkedHashMap
繼承自HashMap
,所以它的底層仍然是基於拉鍊式雜湊結構即由陣列和連結串列或紅黑樹組成。另外,LinkedHashMap
在上面結構的基礎上,增加了一條雙向連結串列,使得上面的結構可以保持鍵值對的插入順序。同時通過對連結串列進行相應的操作,實現了訪問順序相關邏輯。詳細可以檢視:《LinkedHashMap 原始碼詳細分析(JDK1.8)》Hashtable
: 陣列+連結串列組成的,陣列是HashMap
的主體,連結串列則是主要為了解決雜湊衝突而存在的TreeMap
: 紅黑樹(自平衡的排序二叉樹)
Collection 子介面之 List
Arraylist 和 Vector 的區別
ArrayList
是List
的主要實現類,底層使用Object[ ]
儲存,適用於頻繁的查詢工作,執行緒不安全 ;Vector
是List
的古老實現類,底層使用Object[ ]
儲存,執行緒安全的。
Arraylist 與 LinkedList 區別
- 是否保證執行緒安全:
ArrayList
和LinkedList
都是不同步的,也就是不保證執行緒安全; - 底層資料結構:
Arraylist
底層使用的是Object
陣列;LinkedList
底層使用的是 雙向連結串列 資料結構(JDK1.6 之前為迴圈連結串列,JDK1.7 取消了迴圈。注意雙向連結串列和雙向迴圈連結串列的區別,下面有介紹到!) - 插入和刪除是否受元素位置的影響: ①
ArrayList
採用陣列儲存,所以插入和刪除元素的時間複雜度受元素位置的影響。 比如:執行add(E e)
方法的時候,ArrayList
會預設在將指定的元素追加到此列表的末尾,這種情況時間複雜度就是 O(1)。但是如果要在指定位置 i 插入和刪除元素的話(add(int index, E element)
)時間複雜度就為 O(n-i)。因為在進行上述操作的時候集合中第 i 和第 i 個元素之後的(n-i)個元素都要執行向後位/向前移一位的操作。 ②LinkedList
採用連結串列儲存,所以對於add(E e)
方法的插入,刪除元素時間複雜度不受元素位置的影響,近似 O(1),如果是要在指定位置i
插入和刪除元素的話((add(int index, E element)
) 時間複雜度近似為o(n))
因為需要先移動到指定位置再插入。 - 是否支援快速隨機訪問:
LinkedList
不支援高效的隨機元素訪問,而ArrayList
支援。快速隨機訪問就是通過元素的序號快速獲取元素物件(對應於get(int index)
方法)。 - 記憶體空間佔用: ArrayList 的空 間浪費主要體現在在 list 列表的結尾會預留一定的容量空間,而 LinkedList 的空間花費則體現在它的每一個元素都需要消耗比 ArrayList 更多的空間(因為要存放直接後繼和直接前驅以及資料)。
雙向連結串列和雙向迴圈連結串列
雙向連結串列: 包含兩個指標,一個 prev 指向前一個節點,一個 next 指向後一個節點。
雙向迴圈連結串列: 最後一個節點的 next 指向 head,而 head 的 prev 指向最後一個節點,構成一個環。
Collection 子介面之 Set
comparable 和 Comparator 的區別
comparable
介面實際上是出自java.lang
包 它有一個compareTo(Object obj)
方法用來排序comparator
介面實際上是出自 java.util 包它有一個compare(Object obj1, Object obj2)
方法用來排序
一般我們需要對一個集合使用自定義排序時,我們就要重寫compareTo()
方法或compare()
方法,當我們需要對某一個集合實現兩種排序方式,比如一個 song 物件中的歌名和歌手名分別採用一種排序方法的話,我們可以重寫compareTo()
方法和使用自制的Comparator
方法或者以兩個 Comparator 來實現歌名排序和歌星名排序,第二種代表我們只能使用兩個引數版的 Collections.sort()
.
Comparator 定製排序
ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(-1);
arrayList.add(3);
arrayList.add(3);
arrayList.add(-5);
arrayList.add(7);
arrayList.add(4);
arrayList.add(-9);
arrayList.add(-7);
System.out.println("原始陣列:");
System.out.println(arrayList);
// void reverse(List list):反轉
Collections.reverse(arrayList);
System.out.println("Collections.reverse(arrayList):");
System.out.println(arrayList);
// void sort(List list),按自然排序的升序排序
Collections.sort(arrayList);
System.out.println("Collections.sort(arrayList):");
System.out.println(arrayList);
// 定製排序的用法
Collections.sort(arrayList, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
System.out.println("定製排序後:");
System.out.println(arrayList);Copy to clipboardErrorCopied
輸入如下:
原始陣列:
[-1, 3, 3, -5, 7, 4, -9, -7]
Collections.reverse(arrayList):
[-7, -9, 4, 7, -5, 3, 3, -1]
Collections.sort(arrayList):
[-9, -7, -5, -1, 3, 3, 4, 7]
定製排序後:
[7, 4, 3, 3, -1, -5, -7, -9]Copy to clipboardErrorCopied
無序性和不可重複性的含義
什麼是無序性:無序性不等於隨機性 ,無序性是指儲存的資料在底層陣列中並非按照陣列索引的順序新增 ,而是根據資料的雜湊值決定的。
什麼是不可重複性:不可重複性是指新增的元素按照 equals()判斷時 ,返回 false,需要同時重寫 equals()方法和 HashCode()方法。
HashSet、LinkedHashSet、TreeSet 的區別
-
HashSet
是Set
介面的主要實現類 ,HashSet
的底層是HashMap
,執行緒不安全的,可以儲存 null 值; -
LinkedHashSet
是HashSet
的子類,能夠按照新增的順序遍歷; -
TreeSet
底層使用紅黑樹,能夠按照新增元素的順序進行遍歷,排序的方式有自然排序和定製排序。
Map 介面
HashMap 和 Hashtable 的區別
- 執行緒是否安全:
HashMap
是非執行緒安全的,HashTable
是執行緒安全的,因為HashTable
內部的方法基本都經過synchronized
修飾。(如果你要保證執行緒安全的話就使用ConcurrentHashMap
吧!); - 效率: 因為執行緒安全的問題,
HashMap
要比HashTable
效率高一點。另外,HashTable
基本被淘汰,不要在程式碼中使用它; - 對 Null key 和 Null value 的支援:
HashMap
可以儲存 null 的 key 和 value,但 null 作為鍵只能有一個,null 作為值可以有多個;HashTable 不允許有 null 鍵和 null 值,否則會丟擲NullPointerException
。 - 初始容量大小和每次擴充容量大小的不同 : ① 建立時如果不指定容量初始值,
Hashtable
預設的初始大小為 11,之後每次擴充,容量變為原來的 2n+1。HashMap
預設的初始化大小為 16。之後每次擴充,容量變為原來的 2 倍。② 建立時如果給定了容量初始值,那麼 Hashtable 會直接使用你給定的大小,而HashMap
會將其擴充為 2 的冪次方大小(HashMap
中的tableSizeFor()
方法保證,下面給出了原始碼)。也就是說HashMap
總是使用 2 的冪作為雜湊表的大小,後面會介紹到為什麼是 2 的冪次方。 - 底層資料結構: JDK1.8 以後的
HashMap
在解決雜湊衝突時有了較大的變化,當連結串列長度大於閾值(預設為 8)(將連結串列轉換成紅黑樹前會判斷,如果當前陣列的長度小於 64,那麼會選擇先進行陣列擴容,而不是轉換為紅黑樹)時,將連結串列轉化為紅黑樹,以減少搜尋時間。Hashtable 沒有這樣的機制。
HashMap
中帶有初始容量的建構函式:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}Copy to clipboardErrorCopied
下面這個方法保證了 HashMap
總是使用 2 的冪作為雜湊表的大小。
/**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}Copy to clipboardErrorCopied
HashMap 和 HashSet 區別
如果你看過 HashSet
原始碼的話就應該知道:HashSet
底層就是基於 HashMap
實現的。(HashSet
的原始碼非常非常少,因為除了 clone()
、writeObject()
、readObject()
是 HashSet
自己不得不實現之外,其他方法都是直接呼叫 HashMap
中的方法。
HashMap |
HashSet |
---|---|
實現了 Map 介面 |
實現 Set 介面 |
儲存鍵值對 | 僅儲存物件 |
呼叫 put() 向 map 中新增元素 |
呼叫 add() 方法向 Set 中新增元素 |
HashMap 使用鍵(Key)計算 hashcode |
HashSet 使用成員物件來計算 hashcode 值,對於兩個物件來說 hashcode 可能相同,所以 equals() 方法用來判斷物件的相等性 |
HashMap 和 TreeMap 區別
TreeMap
和HashMap
都繼承自AbstractMap
,但是需要注意的是TreeMap
它還實現了NavigableMap
介面和SortedMap
介面。
實現 NavigableMap
介面讓 TreeMap
有了對集合內元素的搜尋的能力。
實現SortMap
介面讓 TreeMap
有了對集合中的元素根據鍵排序的能力。預設是按 key 的升序排序,不過我們也可以指定排序的比較器。示例程式碼如下:
/**
* @author shuang.kou
* @createTime 2020年06月15日 17:02:00
*/
public class Person {
private Integer age;
public Person(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public static void main(String[] args) {
TreeMap<Person, String> treeMap = new TreeMap<>(new Comparator<Person>() {
@Override
public int compare(Person person1, Person person2) {
int num = person1.getAge() - person2.getAge();
return Integer.compare(num, 0);
}
});
treeMap.put(new Person(3), "person1");
treeMap.put(new Person(18), "person2");
treeMap.put(new Person(35), "person3");
treeMap.put(new Person(16), "person4");
treeMap.entrySet().stream().forEach(personStringEntry -> {
System.out.println(personStringEntry.getValue());
});
}
}Copy to clipboardErrorCopied
輸入如下:
person1
person4
person2
person3Copy to clipboardErrorCopied
可以看出,TreeMap
中的元素已經是按照 Person
的 age 欄位的升序來排列了。
上面,我們是通過傳入匿名內部類的方式實現的,你可以將程式碼替換成 Lambda 表示式實現的方式:
TreeMap<Person, String> treeMap = new TreeMap<>((person1, person2) -> {
int num = person1.getAge() - person2.getAge();
return Integer.compare(num, 0);
});Copy to clipboardErrorCopied
綜上,相比於HashMap
來說 TreeMap
主要多了對集合中的元素根據鍵排序的能力以及對集合內元素的搜尋的能力。
HashSet 如何檢查重複
當你把物件加入
HashSet
時,HashSet
會先計算物件的hashcode
值來判斷物件加入的位置,同時也會與其他加入的物件的hashcode
值作比較,如果沒有相符的hashcode
,HashSet
會假設物件沒有重複出現。但是如果發現有相同hashcode
值的物件,這時會呼叫equals()
方法來檢查hashcode
相等的物件是否真的相同。如果兩者相同,HashSet
就不會讓加入操作成功。
hashCode()
與 equals()
的相關規定:
- 如果兩個物件相等,則
hashcode
一定也是相同的 - 兩個物件相等,對兩個
equals()
方法返回 true - 兩個物件有相同的
hashcode
值,它們也不一定是相等的 - 綜上,
equals()
方法被覆蓋過,則hashCode()
方法也必須被覆蓋 hashCode()
的預設行為是對堆上的物件產生獨特值。如果沒有重寫hashCode()
,則該 class 的兩個物件無論如何都不會相等(即使這兩個物件指向相同的資料)。
== 與 equals 的區別:
對於基本型別來說,== 比較的是值是否相等;
對於引用型別來說,== 比較的是兩個引用是否指向同一個物件地址(兩者在記憶體中存放的地址(堆記憶體地址)是否指向同一個地方);
對於引用型別(包括包裝型別)來說,equals 如果沒有被重寫,對比它們的地址是否相等;如果 equals()方法被重寫(例如 String),則比較的是地址裡的內容。
HashMap 的底層實現
JDK1.8 之前 HashMap
底層是 陣列和連結串列 結合在一起使用也就是 連結串列雜湊。HashMap 通過 key 的 hashCode 經過擾動函式處理過後得到 hash 值,然後通過 (n - 1) & hash 判斷當前元素存放的位置(這裡的 n 指的是陣列的長度),如果當前位置存在元素的話,就判斷該元素與要存入的元素的 hash 值以及 key 是否相同,如果相同的話,直接覆蓋,不相同就通過拉鍊法解決衝突。
所謂擾動函式指的就是 HashMap 的 hash 方法。使用 hash 方法也就是擾動函式是為了防止一些實現比較差的 hashCode() 方法 換句話說使用擾動函式之後可以減少碰撞。
JDK 1.8 的 hash 方法 相比於 JDK 1.7 hash 方法更加簡化,但是原理不變。
static final int hash(Object key) {
int h;
// key.hashCode():返回雜湊值也就是hashcode
// ^ :按位異或
// >>>:無符號右移,忽略符號位,空位都以0補齊
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}Copy to clipboardErrorCopied
對比一下 JDK1.7 的 HashMap 中的 hash 方法原始碼.
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}Copy to clipboardErrorCopied
相比於 JDK1.8 的 hash 方法 ,JDK 1.7 的 hash 方法的效能會稍差一點點,因為畢竟擾動了 4 次。
所謂 “拉鍊法” 就是:將連結串列和陣列相結合。也就是說建立一個連結串列陣列,陣列中每一格就是一個連結串列。若遇到雜湊衝突,則將衝突的值加到連結串列中即可。
相比於 JDK1.7 版本, JDK1.8 之後在解決雜湊衝突時有了較大的變化,當連結串列長度大於閾值(預設為 8)(將連結串列轉換成紅黑樹前會判斷,如果當前陣列的長度小於 64,那麼會選擇先進行陣列擴容,而不是轉換為紅黑樹)時,將連結串列轉化為紅黑樹,以減少搜尋時間。
TreeMap、TreeSet 以及 JDK1.8 之後的 HashMap 底層都用到了紅黑樹。紅黑樹就是為了解決二叉查詢樹的缺陷,因為二叉查詢樹在某些情況下會退化成一個線性結構。
HashMap 的長度為什麼是 2 的冪次方
為了能讓 HashMap 存取高效,儘量較少碰撞,也就是要儘量把資料分配均勻。我們上面也講到了過了,Hash 值的範圍值-2147483648 到 2147483647,前後加起來大概 40 億的對映空間,只要雜湊函式對映得比較均勻鬆散,一般應用是很難出現碰撞的。但問題是一個 40 億長度的陣列,記憶體是放不下的。所以這個雜湊值是不能直接拿來用的。用之前還要先做對陣列的長度取模運算,得到的餘數才能用來要存放的位置也就是對應的陣列下標。這個陣列下標的計算方法是“ (n - 1) & hash
”。(n 代表陣列長度)。這也就解釋了 HashMap 的長度為什麼是 2 的冪次方。
這個演算法應該如何設計呢?
我們首先可能會想到採用%取餘的操作來實現。但是,重點來了:“取餘(%)操作中如果除數是 2 的冪次則等價於與其除數減一的與(&)操作(也就是說 hash%length==hash&(length-1)的前提是 length 是 2 的 n 次方;)。” 並且 採用二進位制位操作 &,相對於%能夠提高運算效率,這就解釋了 HashMap 的長度為什麼是 2 的冪次方。
HashMap 多執行緒操作導致死迴圈問題
主要原因在於併發下的 Rehash 會造成元素之間會形成一個迴圈連結串列。不過,jdk 1.8 後解決了這個問題,但是還是不建議在多執行緒下使用 HashMap,因為多執行緒下使用 HashMap 還是會存在其他問題比如資料丟失。併發環境下推薦使用 ConcurrentHashMap 。
HashMap 常見的遍歷方式
ConcurrentHashMap 和 Hashtable 的區別
ConcurrentHashMap
和 Hashtable
的區別主要體現在實現執行緒安全的方式上不同。
- 底層資料結構: JDK1.7 的
ConcurrentHashMap
底層採用 分段的陣列+連結串列 實現,JDK1.8 採用的資料結構跟HashMap1.8
的結構一樣,陣列+連結串列/紅黑二叉樹。Hashtable
和 JDK1.8 之前的HashMap
的底層資料結構類似都是採用 陣列+連結串列 的形式,陣列是 HashMap 的主體,連結串列則是主要為了解決雜湊衝突而存在的; - 實現執行緒安全的方式(重要): ① 在 JDK1.7 的時候,
ConcurrentHashMap
(分段鎖) 對整個桶陣列進行了分割分段(Segment
),每一把鎖只鎖容器其中一部分資料,多執行緒訪問容器裡不同資料段的資料,就不會存在鎖競爭,提高併發訪問率。 到了 JDK1.8 的時候已經摒棄了Segment
的概念,而是直接用Node
陣列+連結串列+紅黑樹的資料結構來實現,併發控制使用synchronized
和 CAS 來操作。(JDK1.6 以後 對synchronized
鎖做了很多優化) 整個看起來就像是優化過且執行緒安全的HashMap
,雖然在 JDK1.8 中還能看到Segment
的資料結構,但是已經簡化了屬性,只是為了相容舊版本;②Hashtable
(同一把鎖) :使用synchronized
來保證執行緒安全,效率非常低下。當一個執行緒訪問同步方法時,其他執行緒也訪問同步方法,可能會進入阻塞或輪詢狀態,如使用 put 新增元素,另一個執行緒不能使用 put 新增元素,也不能使用 get,競爭會越來越激烈效率越低。
JDK1.8 的 ConcurrentHashMap
不在是 Segment 陣列 + HashEntry 陣列 + 連結串列,而是 Node 陣列 + 連結串列 / 紅黑樹。不過,Node 只能用於連結串列的情況,紅黑樹的情況需要使用 TreeNode
。當衝突連結串列達到一定長度時,連結串列會轉換成紅黑樹。
ConcurrentHashMap 執行緒安全的底層實現
上面有示意圖。
-
JDK 1.7 的 ConcurrentHashMap
首先將資料分為一段一段的儲存,然後給每一段資料配一把鎖,當一個執行緒佔用鎖訪問其中一個段資料時,其他段的資料也能被其他執行緒訪問。
ConcurrentHashMap
是由Segment
陣列結構和HashEntry
陣列結構組成。Segment 實現了
ReentrantLock
,所以Segment
是一種可重入鎖,扮演鎖的角色。HashEntry
用於儲存鍵值對資料。static class Segment<K,V> extends ReentrantLock implements Serializable { }Copy to clipboardErrorCopied
一個
ConcurrentHashMap
裡包含一個Segment
陣列。Segment
的結構和HashMap
類似,是一種陣列和連結串列結構,一個Segment
包含一個HashEntry
陣列,每個HashEntry
是一個連結串列結構的元素,每個Segment
守護著一個HashEntry
陣列裡的元素,當對HashEntry
陣列的資料進行修改時,必須首先獲得對應的Segment
的鎖。 -
JDK 1.8 的 ConcurrentHashMap
ConcurrentHashMap
取消了Segment
分段鎖,採用 CAS 和synchronized
來保證併發安全。資料結構跟 HashMap1.8 的結構類似,陣列+連結串列/紅黑二叉樹。Java 8 在連結串列長度超過一定閾值(8)時將連結串列(定址時間複雜度為 O(N))轉換為紅黑樹(定址時間複雜度為 O(log(N)))synchronized
只鎖定當前連結串列或紅黑二叉樹的首節點,這樣只要 hash 不衝突,就不會產生併發,效率又提升 N 倍。
Collections 工具類
排序
void reverse(List list)//反轉
void shuffle(List list)//隨機排序
void sort(List list)//按自然排序的升序排序
void sort(List list, Comparator c)//定製排序,由Comparator控制排序邏輯
void swap(List list, int i , int j)//交換兩個索引位置的元素
void rotate(List list, int distance)//旋轉。當distance為正數時,將list後distance個元素整體移到前面。當distance為負數時,將 list的前distance個元素整體移到後面Copy to clipboardErrorCopied
查詢、替換操作
int binarySearch(List list, Object key)//對List進行二分查詢,返回索引,注意List必須是有序的
int max(Collection coll)//根據元素的自然順序,返回最大的元素。 類比int min(Collection coll)
int max(Collection coll, Comparator c)//根據定製排序,返回最大元素,排序規則由Comparatator類控制。類比int min(Collection coll, Comparator c)
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素。
int frequency(Collection c, Object o)//統計元素出現次數
int indexOfSubList(List list, List target)//統計target在list中第一次出現的索引,找不到則返回-1,類比int lastIndexOfSubList(List source, list target).
boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替換舊元素Copy to clipboardErrorCopied
更多幹貨請移步:https://antoniopeng.com