Java 容器系列總結


為什么要使用集合


當我們需要保存一組類型相同的數據的時候,我們應該是用一個容器來保存,這個容器就是數組,但是,使用數組存儲對象具有一定的弊端, 因為我們在實際開發中,存儲的數據的類型是多種多樣的,於是,就出現了“集合”,集合同樣也是用來存儲多個數據的。

數組的缺點是一旦聲明之后,長度就不可變了;同時,聲明數組時的數據類型也決定了該數組存儲的數據的類型;而且,數組存儲的數據是有序的、可重復的,特點單一。 但是集合提高了數據存儲的靈活性,Java 集合不僅可以用來存儲不同類型不同數量的對象,還可以保存具有映射關系的數據。

Java 容器概述


從下圖可以看出,在 Java 中除了以 Map 結尾的類之外, 其他類都實現了 Collection 接口。

並且,以 Map 結尾的類都實現了 Map 接口。

img

如何選用集合


主要根據集合的特點來選用,比如我們需要根據鍵值獲取到元素值時就選用 Map 接口下的集合,需要排序時選擇 TreeMap,不需要排序時就選擇 HashMap,需要保證線程安全就選用 ConcurrentHashMap

當我們只需要存放元素值時,就選擇實現Collection 接口的集合,需要保證元素唯一時選擇實現 Set 接口的集合比如 TreeSetHashSet,不需要就選擇實現 List 接口的比如 ArrayListLinkedList,然后再根據實現這些接口的集合的特點來選用。

List、Set、Map 三者的區別


  • List(對付順序的好幫手): 存儲的元素是有序的、可重復的。
  • Set(注重獨一無二的性質): 存儲的元素是無序的、不可重復的。
  • Map(用 Key 來搜索的專家): 使用鍵值對(kye-value)存儲,類似於數學上的函數 y=f(x),“x”代表 key,"y"代表 value,Key 是無序的、不可重復的,value 是無序的、可重復的,每個鍵最多映射到一個值。

底層數據結構

先來看一下 Collection 接口下面的集合。

1. List

  • ArraylistObject[]數組
  • VectorObject[]數組
  • LinkedList: 雙向鏈表(JDK1.6 之前為循環鏈表,JDK1.7 取消了循環)

2. Set

  • HashSet(無序,唯一): 基於 HashMap 實現的,底層采用 HashMap 來保存元素
  • LinkedHashSetLinkedHashSetHashSet 的子類,並且其內部是通過 LinkedHashMap 來實現的。有點類似於我們之前說的 LinkedHashMap 其內部是基於 HashMap 實現一樣,不過還是有一點點區別的
  • TreeSet(有序,唯一): 紅黑樹(自平衡的排序二叉樹)

3. Map

再來看看 Map 接口下面的集合。

  • HashMap: JDK1.8 之前 HashMap 由數組+鏈表組成的,數組是 HashMap 的主體,鏈表則是主要為了解決哈希沖突而存在的(“拉鏈法”解決沖突)。JDK1.8 以后在解決哈希沖突時有了較大的變化,當鏈表長度大於閾值(默認為 8)(將鏈表轉換成紅黑樹前會判斷,如果當前數組的長度小於 64,那么會選擇先進行數組擴容,而不是轉換為紅黑樹)時,將鏈表轉化為紅黑樹,以減少搜索時間
  • LinkedHashMapLinkedHashMap 繼承自 HashMap,所以它的底層仍然是基於拉鏈式散列結構即由數組和鏈表或紅黑樹組成。另外,LinkedHashMap 在上面結構的基礎上,增加了一條雙向鏈表,使得上面的結構可以保持鍵值對的插入順序。同時通過對鏈表進行相應的操作,實現了訪問順序相關邏輯。詳細可以查看:《LinkedHashMap 源碼詳細分析(JDK1.8)》
  • Hashtable: 數組+鏈表組成的,數組是 HashMap 的主體,鏈表則是主要為了解決哈希沖突而存在的
  • TreeMap: 紅黑樹(自平衡的排序二叉樹)

Collection 子接口之 List


Arraylist 和 Vector 的區別

  • ArrayListList 的主要實現類,底層使用 Object[ ]存儲,適用於頻繁的查找工作,線程不安全 ;
  • VectorList 的古老實現類,底層使用 Object[ ] 存儲,線程安全的。

Arraylist 與 LinkedList 區別

  1. 是否保證線程安全: ArrayListLinkedList 都是不同步的,也就是不保證線程安全;
  2. 底層數據結構: Arraylist 底層使用的是 Object 數組LinkedList 底層使用的是 雙向鏈表 數據結構(JDK1.6 之前為循環鏈表,JDK1.7 取消了循環。注意雙向鏈表和雙向循環鏈表的區別,下面有介紹到!)
  3. 插入和刪除是否受元素位置的影響: ① 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))因為需要先移動到指定位置再插入。
  4. 是否支持快速隨機訪問: LinkedList 不支持高效的隨機元素訪問,而 ArrayList 支持。快速隨機訪問就是通過元素的序號快速獲取元素對象(對應於get(int index)方法)。
  5. 內存空間占用: ArrayList 的空 間浪費主要體現在在 list 列表的結尾會預留一定的容量空間,而 LinkedList 的空間花費則體現在它的每一個元素都需要消耗比 ArrayList 更多的空間(因為要存放直接后繼和直接前驅以及數據)。

雙向鏈表和雙向循環鏈表

雙向鏈表: 包含兩個指針,一個 prev 指向前一個節點,一個 next 指向后一個節點。

img

雙向循環鏈表: 最后一個節點的 next 指向 head,而 head 的 prev 指向最后一個節點,構成一個環。

img

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 的區別

  • HashSetSet 接口的主要實現類 ,HashSet 的底層是 HashMap,線程不安全的,可以存儲 null 值;

  • LinkedHashSetHashSet 的子類,能夠按照添加的順序遍歷;

  • TreeSet 底層使用紅黑樹,能夠按照添加元素的順序進行遍歷,排序的方式有自然排序和定制排序。

Map 接口


HashMap 和 Hashtable 的區別

  1. 線程是否安全: HashMap 是非線程安全的,HashTable 是線程安全的,因為 HashTable 內部的方法基本都經過synchronized 修飾。(如果你要保證線程安全的話就使用 ConcurrentHashMap 吧!);
  2. 效率: 因為線程安全的問題,HashMap 要比 HashTable 效率高一點。另外,HashTable 基本被淘汰,不要在代碼中使用它;
  3. 對 Null key 和 Null value 的支持: HashMap 可以存儲 null 的 key 和 value,但 null 作為鍵只能有一個,null 作為值可以有多個;HashTable 不允許有 null 鍵和 null 值,否則會拋出 NullPointerException
  4. 初始容量大小和每次擴充容量大小的不同 : ① 創建時如果不指定容量初始值,Hashtable 默認的初始大小為 11,之后每次擴充,容量變為原來的 2n+1。HashMap 默認的初始化大小為 16。之后每次擴充,容量變為原來的 2 倍。② 創建時如果給定了容量初始值,那么 Hashtable 會直接使用你給定的大小,而 HashMap 會將其擴充為 2 的冪次方大小(HashMap 中的tableSizeFor()方法保證,下面給出了源代碼)。也就是說 HashMap 總是使用 2 的冪作為哈希表的大小,后面會介紹到為什么是 2 的冪次方。
  5. 底層數據結構: 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 區別

TreeMapHashMap 都繼承自AbstractMap ,但是需要注意的是TreeMap它還實現了NavigableMap接口和SortedMap 接口。

img

實現 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 值作比較,如果沒有相符的 hashcodeHashSet 會假設對象沒有重復出現。但是如果發現有相同 hashcode 值的對象,這時會調用equals()方法來檢查 hashcode 相等的對象是否真的相同。如果兩者相同,HashSet 就不會讓加入操作成功。

hashCode()equals() 的相關規定:

  1. 如果兩個對象相等,則 hashcode 一定也是相同的
  2. 兩個對象相等,對兩個 equals() 方法返回 true
  3. 兩個對象有相同的 hashcode 值,它們也不一定是相等的
  4. 綜上,equals() 方法被覆蓋過,則 hashCode() 方法也必須被覆蓋
  5. 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,那么會選擇先進行數組擴容,而不是轉換為紅黑樹)時,將鏈表轉化為紅黑樹,以減少搜索時間。

img

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 常見的遍歷方式

HashMap 的 7 種遍歷方式與性能分析!

ConcurrentHashMap 和 Hashtable 的區別

ConcurrentHashMapHashtable 的區別主要體現在實現線程安全的方式上不同。

  • 底層數據結構: 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,競爭會越來越激烈效率越低。

img

img

img

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM