Java源碼分析之Collections類


GitHub地址:Java-source-code-analysis

概述

Collections 類提供一些常用方法

常用方法

  • addAll:將指定元素加入到指定集合中
  • reverse:反轉指定 List 中元素的順序
  • shuffle:對 List 集合元素進行隨機排序
  • sort:排序
  • swap:交換 list 中指定兩個位置的元素
  • max:返回給定集合中的最大元素
  • min:返回給定集合中的最小元素
  • frequency:返回指定集合中指定元素的出現次數
  • copy:將 src 中的內容複製到 dest 中
  • replaceAll:將列表中出現的所有指定值替換爲另一個值
  • binarySearch:使用二分搜索法在指定列表中搜索指定對象
  • rotate:將指定列表元素按指定距離旋轉
  • indexOfSubList:返回目標列表在指定列表第一次出現的起始位置,如果未找到,返回 -1
  • lastIndexOfSubList:返回目標列表在指定列表最後一次出現的起始位置,如果未找到,返回 -1
  • nCopies:返回 n 個指定對象的副本組成的不可變列表
  • fill:將指定 list 的元素全部替換爲指定值

分類:

  • 增:addAll、nCopies
  • 改:reverse、shuffle、sort、swap、copy、replaceAll、rotate、fill
  • 查:max、min、frequency、binarySearch、indexOfSubList、lastIndexOfSubList

1. addAll

將指定元素加入到指定集合中

聲明

public static <T> boolean addAll(Collection<? super T> c, T... elements)

參數

  • c:元素要插入的集合
  • elements:可變參數列表,要插入到 c 中的元素

返回值

  • true:當調用該方法使集合發生變化

異常

  • UnsupportedOperationException:如果 c 不支持 add() 方法

    String[] strArr = {"1","2","3"};
    List<String> l1 = Arrays.asList(strArr);
    // 拋出UnsupportedOperationException異常,原因是asList返回的是Arrays的內部類ArrayList
    // 而不是 java.util.ArrayList
    Collections.addAll(l1, "2");
    
  • NullPointerException:當 c 不允許空值而 elements 含有一個或多個 null 值,另一種情況是 c 或者 elements 爲空

    List<String> list = null;
    // 拋出NullPointerException異常,因爲list爲空
    Collections.addAll(list, "1", "2", "3");
    
  • IllegalArgumentException:elements 中值的某些屬性不能加入 c

    //TODO

使用示例

代碼

public void addAllTest() {
    List<String> list = new ArrayList<>();
    list.add("1");
    list.add("1");
    System.out.println("使用addAll之前:" + list);
    boolean b = Collections.addAll(list, "2", "2");
    System.out.println("使用addAll之後:" + list + ",返回值爲:" + b);
}

輸出

使用addAll之前:[1, 1]
使用addAll之後:[1, 1, 2, 2],返回值爲:true

源碼分析

public static <T> boolean addAll(Collection<? super T> c, T... elements) {
    // 原始結果爲 false
    boolean result = false;
    // 遍歷elements可變參數列表
    for (T element : elements)
        // 按位或,只有同爲 false,結果才爲 false
        // 只有全部 element 加入失敗,纔會返回 false
        result |= c.add(element);
    return result;
}

2. reverse

反轉指定 List 中元素的順序

聲明

public static void reverse(List<?> list)

參數

  • list:指定被翻轉的列表

返回值

void

異常

  • UnsupportedOperationException:如果指定 list 或者其迭代器不支持 set 操作

    List<String> list = Collections.nCopies(4, "hhhh");
    // 拋出UnsupportedOperationException異常,nCopies返回的是一個Collections類中的靜態內部類
    // 該靜態內部類不存在set方法
    Collections.reverse(list);
    

使用示例

代碼

public void reverseTest() {
    List<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    System.out.println("使用reverse之前:" + list);

    Collections.reverse(list);

    System.out.println("使用reverse之後:" + list);
}

輸出

使用reverse之前:[a, b, c]
使用reverse之後:[c, b, a]

源碼分析

public static void reverse(List<?> list) {
    // 得到 list 中元素個數
    int size = list.size();
    // private static final int REVERSE_THRESHOLD  = 18;
    // List 接口下的子類中,ArrayList 和 Vector 實現了 RandomAccess 接口
    // 如果元素個數小於 18,或者該 List 實現了 RandomAccess 接口,就進行此類反轉
    if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {
        // 直接頭尾交換
        for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)
            swap(list, i, j);
    } else {
        // instead of using a raw type here, it's possible to capture
        // the wildcard but it will require a call to a supplementary
        // private method
        // 使用迭代器交換,listIterator 傳遞的參數是索引
        ListIterator fwd = list.listIterator();
        ListIterator rev = list.listIterator(size);
        for (int i=0, mid=list.size()>>1; i<mid; i++) {
            Object tmp = fwd.next();
            fwd.set(rev.previous());
            rev.set(tmp);
        }
    }
}

3. shuffle

對 List 集合元素進行隨機排序

聲明

public static void shuffle(List<?> list);
public static void shuffle(List<?> list, Random rnd);

參數

  • list:將要進行隨機排序的列表
  • rnd:指定用於隨機排列列表的隨機數

返回值

void

異常

  • UnsupportedOperationException:如果指定 list 或者其迭代器不支持 set 操作

    List<String> list = Collections.nCopies(4, "hhhh");
    // 拋出UnsupportedOperationException異常,nCopies返回的是一個Collections類中的靜態內部類
    // 該靜態內部類不存在set方法
    Collections.shuffle(list);
    

使用示例

代碼

public void shuffleTest() {
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        list.add(i);
    }
    System.out.print("使用shuffle之前:" + list);
    System.out.println();
    Collections.shuffle(list);
    System.out.print("使用shuffle之後:" + list);
    System.out.println();
    Collections.shuffle(list,new Random());
    System.out.print("使用帶隨機種子的shuffle之後:" + list);
    System.out.println();
    Collections.shuffle(list,new Random());
    System.out.print("使用帶隨機種子的shuffle之後:" + list);
}

輸出

使用shuffle之前:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
使用shuffle之後:[9, 6, 1, 8, 4, 7, 3, 2, 5, 0]
使用帶隨機種子的shuffle之後:[1, 6, 5, 9, 4, 0, 2, 3, 7, 8]
使用帶隨機種子的shuffle之後:[0, 4, 9, 7, 1, 3, 8, 2, 6, 5]

源碼分析

public static void shuffle(List<?> list) {
    // private static Random r;
    Random rnd = r;
    // 參數如果爲空,初始化參數
    if (rnd == null)
        r = rnd = new Random(); // harmless race.
    // 調用重載方法
    shuffle(list, rnd);
}
public static void shuffle(List<?> list, Random rnd) {
    // 得到元素個數
    int size = list.size();
    // private static final int SHUFFLE_THRESHOLD  =  5;
    // 元素個數小於 5,或者該 List 實現了 RandomAccess 接口,直接進行隨機打亂
    if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
        // nextInt(i) 生成 [0,i)之間的數
        // 每次 i-1 位置的數和 nextInt(i) 生成的位置的數互換
        for (int i=size; i>1; i--)
            swap(list, i-1, rnd.nextInt(i));
    } else {
        // 將 list 元素轉存進 arr 數組
        Object arr[] = list.toArray();

        // Shuffle array
        // 轉存數組後隨機排序,和上面一樣
        for (int i=size; i>1; i--)
            swap(arr, i-1, rnd.nextInt(i));

        // Dump array back into list
        // instead of using a raw type here, it's possible to capture
        // the wildcard but it will require a call to a supplementary
        // private method
        // 利用迭代器把隨機排序後的數組轉存回原 list
        ListIterator it = list.listIterator();
        for (Object e : arr) {
            it.next();
            it.set(e);
        }
    }
}

4. sort

對指定 list 按默認升序或傳遞的比較器順序排序

聲明

public static <T extends Comparable<? super T>> void sort(List<T> list)
public static <T> void sort(List<T> list, Comparator<? super T> c)

參數

  • list:要排序的列表
  • c:指定列表的排序規則,如果是 null,則爲默認升序

返回值

void

異常

  • ClassCastException:如果列表中的元素不能相互比較

    List list = new ArrayList<>();
    list.add(1);
    list.add("2");
    // ClassCastException String和Integer類型不可以互相比較
    Collections.sort(list);
    
  • UnsupportedOperationException:如果指定列表的列表迭代器不支持 set 操作

    // TODO

  • IllegalArgumentException:如果檢測到列表元素的自然順序違反了可比較的約定

使用示例

代碼

public void sortTest() {
    List<Integer> list = new ArrayList<>();
    list.add(5);
    list.add(2);
    list.add(7);
    System.out.println("使用sort之前:" + list);
    Collections.sort(list);
    System.out.println("使用sort之後:" + list);
    // 降序
    Collections.sort(list, new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    });
    System.out.println("使用sort之後:" + list);
}

輸出

使用sort之前:[5, 2, 7]
使用sort之後:[2, 5, 7]
使用sort之後:[7, 5, 2]

源碼

// Collections 中的sort
public static <T extends Comparable<? super T>> void sort(List<T> list) {
    // 調用 List 接口下的sort
    list.sort(null);
}
public static <T> void sort(List<T> list, Comparator<? super T> c) {
    // 調用 List 接口下的sort,傳入比較器
    list.sort(c);
}
// List 接口中的sort
default void sort(Comparator<? super E> c) {
    // 將 list 轉存到數組中
    Object[] a = this.toArray();
    // 調用 Arrays 類的 sort
    Arrays.sort(a, (Comparator) c);
    // 調用迭代器,將排好序的數組存回 list
    ListIterator<E> i = this.listIterator();
    for (Object e : a) {
        i.next();
        i.set((E) e);
    }
}
// Arrays類中的sort
public static <T> void sort(T[] a, Comparator<? super T> c) {
    // 迭代器爲null時,調用不使用迭代器的方法
    if (c == null) {
        sort(a);
    } else {
        // 兼容老版本,當用戶設置時,使用
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a, c);
        else
            // 調用 TimSort 類的 sort
            TimSort.sort(a, 0, a.length, c, null, 0, 0);
    }
}

之後太複雜了,幾乎涉及了 TimSort 類的所有方法,暫時 //TODO,之後專門分析這個類

5. swap

交換指定 list 的 i、j 位置元素

聲明

public static void swap(List<?> list, int i, int j)

參數

  • list:要交換位置的列表
  • i:要交換的一個元素的索引值
  • j:要交換的另一個元素的索引值

返回值

void

異常

  • IndexOutOfBoundsException:如果 i 或 j 超出範圍(i<0 || i>=list.size() || j<0 || j>=list.size())

    List<Integer> list = new ArrayList<>();
    list.add(5);
    // 拋出IndexOutOfBoundsException異常
    Collections.swap(list,-1,5);
    

使用示例

代碼

public void swapTest(){
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    System.out.println("使用swap之前:" + list);
    Collections.swap(list,0,1);
    System.out.println("使用swap之後:" + list);
}

輸出

使用swap之前:[1, 2, 3, 4]
使用swap之後:[2, 1, 3, 4]

源碼分析

public static void swap(List<?> list, int i, int j) {
    // instead of using a raw type here, it's possible to capture
    // the wildcard but it will require a call to a supplementary
    // private method
    final List l = list;
    // 被final修飾的引用本身不可改變,但是引用所指的對象可以改變
    // l.get(i),得到 i 位置上的值
    // l.set(j, l.get(i)),將得到的值賦值到 j 位置,並返回原來 j 位置的舊值
    // l.set(i, l.set(j, l.get(i))),把得到的 j 的舊值賦值到 i 位置
    l.set(i, l.set(j, l.get(i)));
}

6. max

根據元素自然順序或根據給定比較器規則返回最大值

聲明

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)

參數

  • coll:要返回最大值的集合
  • comp:自定義比較器

返回值

  • T:給定集合按自然順序或者自定義比較器規則得到的最大值

異常

  • ClassCastException:如果集合元素間不可互相比較

    HashSet integers = new HashSet<>();
    integers.add(5);
    integers.add("3");
    // 拋出ClassCastException異常,Integer和String無法比較
    Collections.max(integers);
    
  • NoSuchElementException:如果集合爲空

    HashSet integers = new HashSet<>();
    // 拋出NoSuchElementException異常,集合爲空
    Collections.max(integers);
    

使用示例

代碼

public void maxTest() {
    List<Integer> list1 = new ArrayList<>();
    list1.add(5);
    list1.add(1);
    List<String> list2 = new ArrayList<>();
    list2.add("a");
    list2.add("D");
    System.out.println("list1中最大值爲:" + Collections.max(list1));
    System.out.println("list2中最大值爲:" + Collections.max(list2));
}

輸出

list1中最大值爲:5
list2中最大值爲:a

源碼分析

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) {
    // 拿到迭代器
    Iterator<? extends T> i = coll.iterator();
    // 第一個值作爲備選項
    T candidate = i.next();

    // 逐個比較,如果存在更大的元素就替換備選項
    while (i.hasNext()) {
        T next = i.next();
        if (next.compareTo(candidate) > 0)
            candidate = next;
    }
    // 返回
    return candidate;
}
public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) {
    // 如果比較器爲null,調用另一個max方法
    if (comp==null)
        return (T)max((Collection) coll);

    // 拿到迭代器
    Iterator<? extends T> i = coll.iterator();
    T candidate = i.next();

    while (i.hasNext()) {
        T next = i.next();
        // 使用迭代器的 compare方法
        if (comp.compare(next, candidate) > 0)
            candidate = next;
    }
    return candidate;
}

7. min

根據元素自然順序或根據給定比較器規則返回最小值

聲明

public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll)
public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp)

參數

  • coll:要返回最小值的集合
  • comp:自定義比較器

返回值

  • T:給定集合按自然順序或者自定義比較器規則得到的最小值

異常

  • ClassCastException:如果集合元素間不可互相比較

    HashSet integers = new HashSet<>();
    integers.add(5);
    integers.add("3");
    // 拋出ClassCastException異常,Integer和String無法比較
    Collections.min(integers);
    
  • NoSuchElementException:如果集合爲空

    HashSet integers = new HashSet<>();
    // 拋出NoSuchElementException異常,集合爲空
    Collections.min(integers);
    

使用示例

代碼

public void minTest() {
    List<Integer> list1 = new ArrayList<>();
    list1.add(5);
    list1.add(1);
    List<String> list2 = new ArrayList<>();
    list2.add("a");
    list2.add("D");
    System.out.println("list1中最小值爲:" + Collections.min(list1));
    System.out.println("list2中最小值爲:" + Collections.min(list2));
}

輸出結果

list1中最小值爲:1
list2中最小值爲:D

源碼分析

public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) {
    // 拿到迭代器
    Iterator<? extends T> i = coll.iterator();
    T candidate = i.next();

    while (i.hasNext()) {
        T next = i.next();
        // next < candidate,更新 candidate
        if (next.compareTo(candidate) < 0)
            candidate = next;
    }
    return candidate;
}
public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp) {
    // 如果比較器爲null,使用默認自然順序取最小值
    if (comp==null)
        return (T)min((Collection) coll);

    Iterator<? extends T> i = coll.iterator();
    T candidate = i.next();

    while (i.hasNext()) {
        T next = i.next();
        // 調用比較器的compare方法
        if (comp.compare(next, candidate) < 0)
            candidate = next;
    }
    return candidate;
}

8. frequency

返回指定集合中指定元素的出現次數

聲明

public static int frequency(Collection<?> c, Object o)

參數

  • c:需要確定頻率的指定集合
  • o:頻率待指定的元素

返回值

int:c 中等於 o 的元素數目

異常

  • NullPointerException:c 爲 null

    ArrayList<Object> list = null;
    // 拋出NullPointerException異常
    Collections.frequency(list,1);
    

使用示例

代碼

public void frequencyTest(){
    List<String> list = new ArrayList<>();
    list.add("A");
    list.add("A");
    list.add("A");
    list.add("B");
    list.add("B");
    list.add("C");
    System.out.println("使用frequency得到的結果是:" + Collections.frequency(list,"A"));
}

輸出

使用frequency得到的結果是:3

源碼分析

public static int frequency(Collection<?> c, Object o) {
    int result = 0;
    // 如果o爲null,判斷集合中有多少元素等於null
    if (o == null) {
        for (Object e : c)
            if (e == null)
                result++;
    } else {
        // 否則調用equals判等
        for (Object e : c)
            if (o.equals(e))
                result++;
    }
    return result;
}

9. copy

將一個列表的所有內容複製到另一個列表中

聲明

public static <T> void copy(List<? super T> dest, List<? extends T> src)

參數

  • dest:目標列表
  • src:源列表

返回值

void

異常

  • IndexOutOfBoundsException:如果目標列表太小,不足以容納源列表

    ArrayList<Integer> dest = new ArrayList<>();
    ArrayList<Integer> src = new ArrayList<>();
    src.add(1);
    src.add(2);
    // 拋出IndexOutOfBoundsException異常,因爲 dest.size()<src.size()
    Collections.copy(dest,src);
    
  • UnsupportedOperationException:如果目標列表的迭代器不支持 set 操作

    //TODO

使用示例

代碼

public void copyTest(){
    ArrayList<String> dest = new ArrayList<>();
    ArrayList<String> src = new ArrayList<>();
    dest.add("1");
    dest.add("2");
    dest.add("3");
    src.add("6");
    src.add("7");
    System.out.println("未使用copy之前:" + dest);
    Collections.copy(dest,src);
    System.out.println("使用copy之後:" + dest);
}

輸出結果

未使用copy之前:[1, 2, 3]
使用copy之後:[6, 7, 3]

源碼分析

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    // 源列表集合的大小
    int srcSize = src.size();
    // 如果目標列表集合個數小於源列表集合個數,拋出異常
    if (srcSize > dest.size())
        throw new IndexOutOfBoundsException("Source does not fit in dest");

    // private static final int COPY_THRESHOLD = 10;
    // 如果源列表集合大小小於10,或者源列表和目標列表都實現了RandomAccess接口
    // 使用直接 set 的方式複製
    if (srcSize < COPY_THRESHOLD ||
        (src instanceof RandomAccess && dest instanceof RandomAccess)) {
        // 從下標0開始,複製源列表到目標列表
        for (int i=0; i<srcSize; i++)
            dest.set(i, src.get(i));
    } else {
        // 否則使用迭代器
        ListIterator<? super T> di=dest.listIterator();
        ListIterator<? extends T> si=src.listIterator();
        // 也是從下標0開始,將源列表複製到目標列表
        for (int i=0; i<srcSize; i++) {
            di.next();
            di.set(si.next());
        }
    }
}

10. replaceAll

將列表中出現的所有指定值替換爲另一個值

聲明

public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)

參數

  • list:要進行替換的列表
  • oldVal:要被替換的值
  • newVal:取代oldVal的值

返回值

當list包含一個或多個oldVal時,返回"true",否則返回"false"

異常

  • UnsupportedOperationException:如果目標列表的迭代器不支持 set 操作

    //TODO

使用示例

代碼

public void replaceAllTest(){
    List<String> list = new ArrayList<>();
    list.add("a");
    list.add("a");
    list.add("b");
    list.add("a");
    list.add("c");
    System.out.println("使用replaceAll之前:" + list);
    System.out.println("成功替換後返回:" + Collections.replaceAll(list,"b","h"));
    System.out.println("使用replaceAll之後:" + list);
    System.out.println("未成功替換時,返回:" + Collections.replaceAll(list,"b","h"));
}

輸出結果

使用replaceAll之前:[a, a, b, a, c]
成功替換後返回:true
使用replaceAll之後:[a, a, h, a, c]
未成功替換時,返回:false

源碼分析

public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) {
    // 聲明返回值爲false
    boolean result = false;
    // 列表大小
    int size = list.size();
    //  private static final int REPLACEALL_THRESHOLD = 11;
    // 如果列表大小小於11,或者當前list實現了RandomAccess接口
    if (size < REPLACEALL_THRESHOLD || list instanceof RandomAccess) {
        // 考慮oldVal爲null的情況
        if (oldVal==null) {
            for (int i=0; i<size; i++) {
                if (list.get(i)==null) {
                    // 在舊值的位置賦值新值
                    list.set(i, newVal);
                    // 返回值置爲true
                    result = true;
                }
            }
        } else {
            // 返回值不爲 null時,調用equals方法比較
            for (int i=0; i<size; i++) {
                if (oldVal.equals(list.get(i))) {
                    list.set(i, newVal);
                    result = true;
                }
            }
        }
    } else { // 列表大小大於等於11,且該list未實現RandomAccess接口,調用迭代器替換
        ListIterator<T> itr=list.listIterator();
        // oldVal爲null的情況
        if (oldVal==null) {
            for (int i=0; i<size; i++) {
                if (itr.next()==null) {
                    itr.set(newVal);
                    result = true;
                }
            }
        } else { // oldVal不爲null的情況
            for (int i=0; i<size; i++) {
                if (oldVal.equals(itr.next())) {
                    itr.set(newVal);
                    result = true;
                }
            }
        }
    }
    return result;
}

11. binarySearch

使用二分搜素法在指定列表搜索指定對象。

在調用該方法之前,事先必須根據自然順序或比較器順序按升序排序,否則不確保能得到正確的答案。

且當列表存在等於指定對象的多個元素,則不能保證找到哪個元素

聲明

public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key)
public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)

參數

  • list:要搜索的列表
  • key:要查找的元素
  • c:用於排序列表的比較器

返回值

如果key存在list中,返回key所在索引;如果不存在,返回 ( - ( 插入點位置 ) - 1)

異常

  • ClassCastException:如果傳入的key與list元素不能互相比較

    List list = new ArrayList<>();
    list.add("1");
    list.add("3");
    // 拋出ClassCastException異常,Integer和String無法互相比較
    Collections.binarySearch(list,2);
    

使用示例

代碼

public void binarySearchTest() {
    List<Integer> list = new ArrayList<>();
    list.add(0);
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(6);
    System.out.println("查找2的索引爲:" + Collections.binarySearch(list, 2));
    // 如果沒找到,返回 ( -(插入點位置) - 1)
    // 插入位置應該是 5,-5-1 = -6
    System.out.println("查找5的索引爲:" + Collections.binarySearch(list, 5));
}

輸出結果

查找2的索引爲:2
查找5的索引爲:-6

源碼分析

public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) {
    // private static final int BINARYSEARCH_THRESHOLD = 5000;
    // 如果該list實現了RandomAccess接口,或者該list的大小小於5000
    // 使用索引二分搜索法
    if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
        return Collections.indexedBinarySearch(list, key);
    else // 否則使用迭代器二進制搜索法
        return Collections.iteratorBinarySearch(list, key);
}

public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) {
    // 如果比較器爲null,調用另一個二分搜索法
    if (c==null)
        return binarySearch((List<? extends Comparable<? super T>>) list, key);

    // private static final int BINARYSEARCH_THRESHOLD = 5000;
    // 如果該list實現了RandomAccess接口,或者該list的大小小於5000
    // 使用索引二分搜索法,並傳入比較器c
    if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
        return Collections.indexedBinarySearch(list, key, c);
    else // 否則使用迭代器二進制搜索法,並傳入比較器c
        return Collections.iteratorBinarySearch(list, key, c);
}
// 不帶比較器的indexedBinarySearch
private static <T> int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {
    // 二分搜索法
    int low = 0;
    int high = list.size()-1;
	
    while (low <= high) {
        // 中間索引
        int mid = (low + high) >>> 1;
        // 用list的get方法查找中間值
        Comparable<? super T> midVal = list.get(mid);
        // 比較中間值
        int cmp = midVal.compareTo(key);
		// 重置上界和下界
        if (cmp < 0)
            low = mid + 1;
        else if (cmp > 0)
            high = mid - 1;
        else
            return mid; // key found
    }
    // 沒有找到元素,返回負值
    return -(low + 1);  // key not found
}

// 帶比較器的indexedBinarySearch
private static <T> int indexedBinarySearch(List<? extends T> l, T key, Comparator<? super T> c) {
    //  二分搜索法
    int low = 0;
    int high = l.size()-1;

    while (low <= high) {
        int mid = (low + high) >>> 1;
        //用list的get方法查找中間值
        T midVal = l.get(mid);
        // 這用的是比較器的compare方法
        int cmp = c.compare(midVal, key);

        if (cmp < 0)
            low = mid + 1;
        else if (cmp > 0)
            high = mid - 1;
        else
            return mid; // key found
    }
    //  沒有找到元素,返回負值
    return -(low + 1);  // key not found
}
// 不帶比較器的iteratorBinarySearch
private static <T> int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
{
    int low = 0;
    int high = list.size()-1;
    ListIterator<? extends Comparable<? super T>> i = list.listIterator();

    while (low <= high) {
        int mid = (low + high) >>> 1;
        // 從迭代器中返回中間值
        Comparable<? super T> midVal = get(i, mid);
        // 比較中間值
        int cmp = midVal.compareTo(key);

        if (cmp < 0)
            low = mid + 1;
        else if (cmp > 0)
            high = mid - 1;
        else
            return mid; // key found
    }
    //  沒有找到元素,返回負值
    return -(low + 1);  // key not found
}
// 帶比較器的iteratorBinarySearch
private static <T> int iteratorBinarySearch(List<? extends T> l, T key, Comparator<? super T> c) {
    int low = 0;
    int high = l.size()-1;
    ListIterator<? extends T> i = l.listIterator();

    while (low <= high) {
        int mid = (low + high) >>> 1;
        // 從迭代器中返回中間值
        T midVal = get(i, mid);
        // 調用迭代器的compare方法
        int cmp = c.compare(midVal, key);

        if (cmp < 0)
            low = mid + 1;
        else if (cmp > 0)
            high = mid - 1;
        else
            return mid; // key found
    }
    //  沒有找到元素,返回負值
    return -(low + 1);  // key not found
}
// 從迭代器中返回中間值的方法
// 通過重定位指定列表,獲取指定位置元素
private static <T> T get(ListIterator<? extends T> i, int index) {
    T obj = null;
    // 返回後序調用next將返回的元素的索引
    int pos = i.nextIndex();
    if (pos <= index) {  // 如果 pos 小,往後找
        do {
            obj = i.next();
        } while (pos++ < index);
    } else { // 如果 pos 大,往前找
        do {
            obj = i.previous();
        } while (--pos > index);
    }
    // 找到值返回
    return obj;
}

12. rotate

將指定列表元素按指定距離旋轉。

距離爲正時,列表元素整體向右移動,超出列表元素大小的元素依次左補齊;反之

聲明

public static void rotate(List<?> list, int distance)

參數

  • list:要被旋轉的列表
  • distance:要旋轉列表的距離

返回值

void

異常

  • UnsupportedOperationException:如果目標列表的迭代器不支持 set 操作

    //TODO

使用示例

代碼

public void rotateTest() {
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        list.add(i);
    }
    System.out.println("未使用rotate前:" + list);
    Collections.rotate(list, 3);
    System.out.println("使用rotate右移3位後:" + list);
    Collections.rotate(list, -3);
    System.out.println("使用rotate左移3位後:" + list);
    Collections.rotate(list, 13);
    System.out.println("使用rotate右移13位後:" + list);
}

輸出結果

未使用rotate前:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
使用rotate右移3位後:[7, 8, 9, 0, 1, 2, 3, 4, 5, 6]
使用rotate左移3位後:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
使用rotate右移13位後:[7, 8, 9, 0, 1, 2, 3, 4, 5, 6]

源碼分析

public static void rotate(List<?> list, int distance) {
    // private static final int ROTATE_THRESHOLD = 100;
    // 如果該list實現了RandomAccess接口,或者該list的大小小於100
    if (list instanceof RandomAccess || list.size() < ROTATE_THRESHOLD)
        rotate1(list, distance);
    else
        rotate2(list, distance);
}
private static <T> void rotate1(List<T> list, int distance) {
    // 得到list的大小
    int size = list.size();
    // 大小爲0直接返回
    if (size == 0)
        return;
    // 對distance取餘,減少旋轉次數
    distance = distance % size;
    // 如果distance小於0,加上size,使distance落在[0,size)上
    if (distance < 0)
        distance += size;
    // distance爲0沒有旋轉的必要,直接返回
    if (distance == 0)
        return;

    for (int cycleStart = 0, nMoved = 0; nMoved != size; cycleStart++) {
        // 拿到cycleStart位置的值
        T displaced = list.get(cycleStart);
        // 存下cycleStart
        int i = cycleStart;
        // 直接將數放到最終位置
        do {
            // 找到cycleStart旋轉後的落腳點
            i += distance;
            // 如果超過列表大小,減回來,使i落在[0,size)中
            if (i >= size)
                i -= size;
            // 在新位置賦值原來cycleStart位置的值,並拿到舊值
            displaced = list.set(i, displaced);
            nMoved ++;
        } while (i != cycleStart);
    }
}
private static void rotate2(List<?> list, int distance) {
    // 得到list的大小
    int size = list.size();
    // 大小爲0沒有翻轉的必要
    if (size == 0)
        return;
    // distance 是需要從列表後面移到列表前的數的個數
    // mid 計算的是移動後依然還處於列表後面的數
    // ex:1 2 3 4 5,distance = 2
    // mid 是指 1 2 3,這 3 個數未被重新移動到列表開頭
    int mid =  -distance % size;
    if (mid < 0)
        mid += size;
    if (mid == 0)
        return;

    // 通過mid進行劃分,根據線性代數原理
    // ((AB)^T)^T = AB = (B^TA^T)^T
    // 逆轉 [0,mid)
    reverse(list.subList(0, mid));
    // 逆轉 [mid,size)
    reverse(list.subList(mid, size));
    // 逆轉整個列表
    reverse(list);
}

13. indexOfSubList

返回目標列表在指定列表第一次出現的起始位置,如果未找到,返回 -1

聲明

public static int indexOfSubList(List<?> source, List<?> target)

參數

  • source:搜索target第一次出現的列表
  • target:要在source中作爲子列表搜索的列表

返回值

返回target在source中首次出現的下標,如果target未找到,返回-1

異常

使用示例

代碼

 public void indexOfSubListTest() {
     List<Integer> source = new ArrayList<>();
     for (int i = 0; i < 10; i++) {
         source.add(i);
     }
     source.add(2);
     source.add(3);
     List<Integer> target = new ArrayList<>();
     target.add(2);
     target.add(3);
     System.out.println("target在srouce中第一次出現:" + Collections.indexOfSubList(source, target));
     target.add(6);
     System.out.println("target在source未出現:" +  Collections.indexOfSubList(source, target));
 }

輸出結果

目標list在srouce中第一次出現:2
如果目標list在source未出現:-1

源碼分析

public static int indexOfSubList(List<?> source, List<?> target) {
    int sourceSize = source.size();
    int targetSize = target.size();
    // 最大需要比較的長度
    int maxCandidate = sourceSize - targetSize;

    // private static final int INDEXOFSUBLIST_THRESHOLD = 35;
    // 如果 source的大小大於35,或者source和target都實現了RandomAccess接口
    if (sourceSize < INDEXOFSUBLIST_THRESHOLD ||
        (source instanceof RandomAccess&&target instanceof RandomAccess)) {
        // 標籤
        nextCand:
        // candidate 從 0 開始
        for (int candidate = 0; candidate <= maxCandidate; candidate++) {
            for (int i=0, j=candidate; i<targetSize; i++, j++)
                // 如果 target.get(i)和source.get(j)不相等,結束此次以ij爲變量的循環
                if (!eq(target.get(i), source.get(j)))
                    continue nextCand;  // Element mismatch, try next cand
            // 如果全都相等,直接返回索引
            return candidate;  // All elements of candidate matched target
        }
    } else {  // Iterator version of above algorithm
        // 否則使用迭代器
        ListIterator<?> si = source.listIterator();
        nextCand:
        // candidate 從 0 開始
        for (int candidate = 0; candidate <= maxCandidate; candidate++) {
            ListIterator<?> ti = target.listIterator();
            for (int i=0; i<targetSize; i++) {
                // 如果不相等,迭代器回退
                if (!eq(ti.next(), si.next())) {
                    // Back up source iterator to next candidate
                    for (int j=0; j<i; j++)
                        si.previous();
                    continue nextCand;
                }
            }
            return candidate;
        }
    }
    // 未找到,返回 -1
    return -1;  // No candidate matched the target
}
static boolean eq(Object o1, Object o2) {
    return o1==null ? o2==null : o1.equals(o2);
}

14. lastIndexOfSubList

返回目標列表在指定列表最後一次出現的起始位置,如果未找到,返回 -1

聲明

public static int lastIndexOfSubList(List<?> source, List<?> target)

參數

  • source:搜索target最後一次出現的列表
  • target:要在source中作爲子列表搜索的列表

返回值

返回target在source中最後一次出現的下標,如果target未找到,返回-1

異常

使用示例

代碼

public void lastIndexOfSubListTest() {
     List<Integer> source = new ArrayList<>();
     for (int i = 0; i < 10; i++) {
         source.add(i);
     }
     source.add(2);
     source.add(3);
     List<Integer> target = new ArrayList<>();
     target.add(2);
     target.add(3);
     System.out.println("target在srouce中最後一次出現:" + Collections.lastIndexOfSubList(source, target));
     target.add(6);
     System.out.println("target在source未出現:" + Collections.lastIndexOfSubList(source, target));
 }

輸出結果

target在srouce中最後一次出現:10
target在source未出現:-1

源碼分析

public static int lastIndexOfSubList(List<?> source, List<?> target) {
    int sourceSize = source.size();
    int targetSize = target.size();
    // 最大需要比較的長度
    int maxCandidate = sourceSize - targetSize;

    // private static final int INDEXOFSUBLIST_THRESHOLD = 35;
    // source大小小於35,或者source實現了RandomAccess接口
    if (sourceSize < INDEXOFSUBLIST_THRESHOLD ||
        source instanceof RandomAccess) {   // Index access version
        // 標籤
        nextCand:
        // candidate 從最大需要比較的長度maxCandidate開始
        for (int candidate = maxCandidate; candidate >= 0; candidate--) {
            for (int i=0, j=candidate; i<targetSize; i++, j++)
                // 如果target.get(i)和source.get(j)不等,該ij循環結束,繼續下一輪candidate循環
                if (!eq(target.get(i), source.get(j)))
                    continue nextCand;  // Element mismatch, try next cand
            // 找到了直接返回,所以是最後一個
            return candidate;  // All elements of candidate matched target
        }
    } else {  // Iterator version of above algorithm
        // sourceSize<targetSize,肯定找不到,直接返回-1
        if (maxCandidate < 0)
            return -1;
        // 使用迭代器指向maxCandidate位置
        ListIterator<?> si = source.listIterator(maxCandidate);
        nextCand:
        // candidate從maxCandidate開始
        for (int candidate = maxCandidate; candidate >= 0; candidate--) {
            ListIterator<?> ti = target.listIterator();
            for (int i=0; i<targetSize; i++) {
                // 如果不等,退出此輪i循環,且迭代器回退
                if (!eq(ti.next(), si.next())) {
                    if (candidate != 0) {
                        // Back up source iterator to next candidate
                        for (int j=0; j<=i+1; j++)
                            si.previous();
                    }
                    continue nextCand;
                }
            }
            return candidate;
        }
    }
    // 沒找到返回-1
    return -1;  // No candidate matched the target
}
 // 和 indexOfSubList用的同一個eq方法
static boolean eq(Object o1, Object o2) {
    return o1==null ? o2==null : o1.equals(o2);
}

15. nCopies

返回 n 個指定對象的副本組成的不可變列表

聲明

public static <T> List<T> nCopies(int n, T o)

參數

  • n:返回列表中元素的個數
  • o:返回列表中重複出現的元素

返回值

由 n 個 o 組成的不可變列表

異常

  • IllegalArgumentException:如果 n < 0

    // 拋出IllegalArgumentException異常
    Collections.nCopies(-2,1);
    

使用示例

代碼

public void nCopiesTest(){
    List<String> list1 = Collections.nCopies(4, "hhhh");
    System.out.println("list1得到的是:" + list1);
    List<Integer> list2 = Collections.nCopies(3, 2);
    System.out.println("list2得到的是:" + list2);
}

輸出結果

list1得到的是:[hhhh, hhhh, hhhh, hhhh]
list2得到的是:[2, 2, 2]

源碼分析

public static <T> List<T> nCopies(int n, T o) {
    // n<0拋出異常
    if (n < 0)
        throw new IllegalArgumentException("List length = " + n);
    // 創建一個靜態內部類
    return new CopiesList<>(n, o);
}
private static class CopiesList<E> extends AbstractList<E> implements RandomAccess, Serializable{
    private static final long serialVersionUID = 2739099268398711800L;

    // 該類的兩個成員變量都被 final 修飾
    final int n;
    final E element;

    // 構造方法中已經初始化兩個成員變量
    // 所以該類的 n 和 element已不可變
    CopiesList(int n, E e) {
        assert n >= 0;
        this.n = n;
        element = e;
    }

    public int size() {
        return n;
    }

    public boolean contains(Object obj) {
        return n != 0 && eq(obj, element);
    }

    public int indexOf(Object o) {
        return contains(o) ? 0 : -1;
    }

    public int lastIndexOf(Object o) {
        return contains(o) ? n - 1 : -1;
    }

    public E get(int index) {
        if (index < 0 || index >= n)
            throw new IndexOutOfBoundsException("Index: "+index+
                                                ", Size: "+n);
        return element;
    }

    public Object[] toArray() {
        final Object[] a = new Object[n];
        if (element != null)
            Arrays.fill(a, 0, n, element);
        return a;
    }

    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        final int n = this.n;
        if (a.length < n) {
            a = (T[])java.lang.reflect.Array
                .newInstance(a.getClass().getComponentType(), n);
            if (element != null)
                Arrays.fill(a, 0, n, element);
        } else {
            Arrays.fill(a, 0, n, element);
            if (a.length > n)
                a[n] = null;
        }
        return a;
    }

    public List<E> subList(int fromIndex, int toIndex) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > n)
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
        return new CopiesList<>(toIndex - fromIndex, element);
    }

    // Override default methods in Collection
    @Override
    public Stream<E> stream() {
        return IntStream.range(0, n).mapToObj(i -> element);
    }

    @Override
    public Stream<E> parallelStream() {
        return IntStream.range(0, n).parallel().mapToObj(i -> element);
    }

    @Override
    public Spliterator<E> spliterator() {
        return stream().spliterator();
    }
}

16. fill

將指定 list 的元素全部替換爲指定值

聲明

public static <T> void fill(List<? super T> list, T obj)

參數

  • list:要進行元素替換的列表
  • obj:用於填充指定值的元素

返回值

void

異常

  • UnsupportedOperationException:如果指定 list 或者其迭代器不支持 set 操作

    List<Integer> list = Collections.nCopies(4,2);
    // 拋出UnsupportedOperationException異常,nCopies返回的是一個Collections類中的靜態內部類
    // 該靜態內部類不存在set方法
    Collections.fill(list,1);
    

代碼示例

代碼

public void fillTest() {
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        list.add(i);
    }
    System.out.println("使用fill方法前:" + list);
    Collections.fill(list, 1);
    System.out.println("使用fill方法後:" + list);
}

輸出結果

使用fill方法前:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
使用fill方法後:[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

源碼分析

public static <T> void fill(List<? super T> list, T obj) {
    int size = list.size();

    //  private static final int FILL_THRESHOLD = 25;
    // list的大小小於25或者該list實現了 RandomAccess 接口,使用for循環覆蓋
    if (size < FILL_THRESHOLD || list instanceof RandomAccess) {
        for (int i=0; i<size; i++)
            list.set(i, obj);
    } else {
        // 否則使用迭代器覆蓋
        ListIterator<? super T> itr = list.listIterator();
        for (int i=0; i<size; i++) {
            itr.next();
            itr.set(obj);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章