浅谈 Collections

129 阅读4分钟

1.介绍

java.util.Collections 是 java 集合框架的一个工具类,主要用于 Collection 提供的通用算法;包含有各种有关集合操作的静态方法。此类不能实例化

2.Collections.sort 排序

2.1 使用

// 假设 list 中的内容是 [2,6,1,5,2,9,8]

// 默认排列(正序)
Collections.sort(list);
// 测试结果:1 2 2 5 6 8 9

// 倒序排序
Collections.sort(list, Collections.<String>reverseOrder());
// 测试结果:9 8 6 5 2 2 1

2.2 源码简述

  • Collections.sort 调用的是 List 接口的 sort 默认方法
  • List.sort 方法中最终调用的是 Arrays.sort 方法进行排序
  • Arrays.sort 中根据 LegacyMergeSort.userRequested 选择不同的算法排序(一般为 false)
    • 如果为 true,调用 legacyMergeSort 进行归并排序
    • 如果为 false,调用 TimeSort.sort 方法
      • 如果元素个数小于 32,则使用 二分插入排序
      • 如果元素个数大于 32,则使用 分段排序
// Collections$sort
public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null);
}
// Collections$sort
public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}
// List$sort
default void sort(Comparator<? super E> c) {
    // 容器内元素转换为数组
    Object[] a = this.toArray();
    // 调用 Arrays.sort 方法根据 比较器 进行排序
    Arrays.sort(a, (Comparator) c);
    // 获取容器的列表迭代器
    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) {
    if (c == null) {
        sort(a);
    } else {
        if (LegacyMergeSort.userRequested)
            // 归并排序
            legacyMergeSort(a, c);
        else
            // 调用 TimeSort 的排序方法
            TimSort.sort(a, 0, a.length, c, null, 0, 0);
    }
}
// 倒序排列的比较器
public static <T> Comparator<T> reverseOrder() {
    return (Comparator<T>) ReverseComparator.REVERSE_ORDER;
}

private static class ReverseComparator
    implements Comparator<Comparable<Object>>, Serializable {

    private static final long serialVersionUID = 7207038068494060240L;

    static final ReverseComparator REVERSE_ORDER
        = new ReverseComparator();
    
    // 核心:通过调用 c2 的 compareTo() 方法进行比较,从而返回倒序的结果
    public int compare(Comparable<Object> c1, Comparable<Object> c2) {
        return c2.compareTo(c1);
    }

    private Object readResolve() { return Collections.reverseOrder(); }

    @Override
    public Comparator<Comparable<Object>> reversed() {
        return Comparator.naturalOrder();
    }
}

3.Collections.binarySearch 二分查找

3.1 使用

// 假设 list 中的内容是 [2,5,6,9,12]

Collections.binarySearch(list, 5);
// 结果:1

例子:

2.png

3.2 源码简述

  • 实现了 RandomAccess 接口或容器长度小于 50005000,则调用 indexedBinarySearch()
    • ArraysList 实现了该接口,而 LinkedList 没有实现该接口;如果容器长度太长,通过索引定位元素的方式,对于 LinkedList 来说十分耗时
    • 通过索引获取值
  • 否则调用 iteratorBinarySearch()
    • 通过迭代器获取值
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
    if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
        return Collections.indexedBinarySearch(list, key);
    else
        return Collections.iteratorBinarySearch(list, key);
}
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;
        // 从容器中获取值
        Comparable<? super T> midVal = list.get(mid);
        // 与 key 进行比较
        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
}
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);
        // 与 key 比较
        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
}

4.Collections.shuffle 洗牌算法

4.1 使用

List<String> list = new ArrayList<String>(Arrays.asList("a","b","c","d","e"));


Collections.shuffle(list);
// 结果:[b, a, d, c, e]


Collections.shuffle(list,new Random(2));
// 结果:[b, c, a, d, e]

例子:

Collection.png

4.2 源码简述

private static Random r;

public static void shuffle(List<?> list) {
    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();
    // 如果长度小于 5 或者继承了 RandomAccess 接口,则进入该分支
    if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
        for (int i=size; i>1; i--)
            // 遍历列表,每遍历一个位置,就随机一个新的索引,并交换两者的元素
            swap(list, i-1, rnd.nextInt(i));
    } else {
        // 将列表转换为数组
        Object[] arr = list.toArray();
        
        // 同上一个分支持操作一样
        for (int i=size; i>1; i--)
            swap(arr, i-1, rnd.nextInt(i));
        
        // 通过列表迭代器,将数组中的排列顺序设置回列表
        ListIterator it = list.listIterator();
        for (int i=0; i<arr.length; i++) {
            it.next();
            it.set(arr[i]);
        }
    }
}

5.Collections.rotate 旋转算法

5.1 使用

List<String> list = new LinkedList<String>(Arrays.asList("1","2","3","4","5"));


Collections.rotate(list, 2);
// 结果:[4, 5, 1, 2, 3]

例子:

Collection2.png

Collection3.png

5.2 源码简述

public static void rotate(List<?> list, int distance) {
    if (list instanceof RandomAccess || list.size() < ROTATE_THRESHOLD)
        // 实现了 RandomAccess 接口或者列表长度小于 100,则通过 rotate1() 实现
        rotate1(list, distance);
    else
        // 否则通过 rotate2() 实现
        rotate2(list, distance);
}
private static <T> void rotate1(List<T> list, int distance) {
    int size = list.size();
    if (size == 0)
        return;
    // 获取旋转的位置
    distance = distance % size;
    if (distance < 0)
        distance += size;
    if (distance == 0)
        return;
    
    // cycleStart:旋转开始的索引
    // nMoved:已旋转的元素个数
    for (int cycleStart = 0, nMoved = 0; nMoved != size; cycleStart++) {
        // 获取在旋转开始处的元素
        T displaced = list.get(cycleStart);
        int i = cycleStart;
        do {
            // displaced元素 旋转后到达的位置
            i += distance;
            if (i >= size)
                i -= size;
            // 将 displaced元素 设置在 i 处,并返回 i 处原来的元素
            displaced = list.set(i, displaced);
            nMoved ++;
        } while (i != cycleStart);
    }
}
private static void rotate2(List<?> list, int distance) {
    int size = list.size();
    if (size == 0)
        return;
    // 定位拆链位置:找到旋转后,从首结点开始的链表个数
    // 例如 [1,2,3,4,5],distance=2,旋转后 [4,5,1,2,3],可知 mid=3
    int mid =  -distance % size;
    if (mid < 0)
        mid += size;
    if (mid == 0)
        return;
    
    // 翻转 [0,mid) 链表
    reverse(list.subList(0, mid));
    // 翻转 [mid,size) 链表
    reverse(list.subList(mid, size));
    // 翻转整个链表
    reverse(list);
}

6.Collections.min 和 Collections.max

6.1 使用

List<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6));


Integer min = Collections.min(list);
// 结果:min = 1

Integer max = Collections.max(list);
// 结果:max = 6

6.2 源码简述

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 小,则更新候选元素
        if (next.compareTo(candidate) < 0)
            candidate = next;
    }
    return candidate;
}
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();
        // 如果下一个元素 next 比候选元素 candidate 大,则更新候选元素
        if (next.compareTo(candidate) > 0)
            candidate = next;
    }
    return candidate;
}

7.Collections.replaceAll 替换算法

7.1 使用

List<String> list = new ArrayList<String>(Arrays.asList("7", "2", "8", "8", "6", "8", "5"));
// 原列表:[7, 2, 8, 8, 6, 8, 5]

Collections.replaceAll(list, "8", "9");
// 替换后的列表:[7, 2, 9, 9, 6, 9, 5]

7.2 源码简述

public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) {
    boolean result = false;
    int size = list.size();
    // 当列表长度小于 11 或 list实现了 RandomAccess 接口
    if (size < REPLACEALL_THRESHOLD || list instanceof RandomAccess) {
        // for 循环遍历,并通过索引的方式设置值
        if (oldVal==null) {
            for (int i=0; i<size; i++) {
                if (list.get(i)==null) {
                    list.set(i, newVal);
                    result = true;
                }
            }
        } else {
            for (int i=0; i<size; i++) {
                if (oldVal.equals(list.get(i))) {
                    list.set(i, newVal);
                    result = true;
                }
            }
        }
    } else {
        // for 循环遍历,并通过迭代器的方式设置值
        ListIterator<T> itr=list.listIterator();
        if (oldVal==null) {
            for (int i=0; i<size; i++) {
                if (itr.next()==null) {
                    itr.set(newVal);
                    result = true;
                }
            }
        } else {
            for (int i=0; i<size; i++) {
                if (oldVal.equals(itr.next())) {
                    itr.set(newVal);
                    result = true;
                }
            }
        }
    }
    return result;
}

8.Collections.indexOfSubList 定位列表位置

8.1 使用

List<String> list = new ArrayList<String>(Arrays.asList("7", "2", "8", "6", "8", "5"));

int idx = Collections.indexOfSubList(list, Arrays.asList("8", "6"));
// 结果:2

8.2 源码简述

public static int indexOfSubList(List<?> source, List<?> target) {
    int sourceSize = source.size();
    int targetSize = target.size();
    // 计算最大的开始索引位置
    int maxCandidate = sourceSize - targetSize;
    
    // 如果 source 列表长度小于 35 或者 source 和 target 列表都实现了 RandomAccess 接口
    if (sourceSize < INDEXOFSUBLIST_THRESHOLD ||
        (source instanceof RandomAccess && target instanceof RandomAccess)) {
    nextCand:
        // for 循环遍历开始索引位置
        for (int candidate = 0; candidate <= maxCandidate; candidate++) {
            // 遍历两个列表中值是否相等
            for (int i=0, j=candidate; i<targetSize; i++, j++)
                if (!eq(target.get(i), source.get(j)))
                    // 如果有值不相等,则回到 nextCand 标签处重新开始
                    continue nextCand;
            return candidate;  // All elements of candidate matched target
        }
    } else {
        // 处理逻辑和上面相似,不同点在于通过列表迭代器遍历列表
        ListIterator<?> si = source.listIterator();
    nextCand:
        for (int candidate = 0; candidate <= maxCandidate; candidate++) {
            ListIterator<?> ti = target.listIterator();
            for (int i=0; i<targetSize; i++) {
                if (!eq(ti.next(), si.next())) {
                    // 不相等时,将 si 指针回退到 target 列表开始处
                    for (int j=0; j<i; j++)
                        si.previous();
                    continue nextCand;
                }
            }
            return candidate;
        }
    }
    return -1;  // No candidate matched the target
}