java 集合 之 集合工具类Collections

发布于:2025-08-16 ⋅ 阅读:(22) ⋅ 点赞:(0)

前言

        早期开发者经常需要对集合进行各种操作

        比如排序、查找最大最小值等等

        但是当时没有统一的工具类来处理

        所以导致代码重复且容易出错

        

        java.util.Collections 工具类的引入

        为开发者提供了大量 静态方法 操作集合

        它就像一个经验丰富的助手

        和数组工具类 Arrays 一样

        避免了我们重复造轮子的情况

一、Collections 工具类概述

java.util.Collections 是一个操作集合的工具类

提供大量 静态方法 操作 返回 集合

源码:

// Collections 类的声明
public class Collections {
    // 私有构造函数,防止实例化
    private Collections() {
    }
    
    // ... 大量静态方法
}

通过源码我们可以看出:

  • Collections 是一个很纯粹的工具类
  • 没有继承,没有实现,父类是 Object
  • 构造器私有,不能实例化对象 

注意区分:

        Collections 是集合的工具类

        Collection   是单列集合的根接口

二、核心方法

1、addAll

方法声明:

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

调用者:

        Collections

参数:

        Collection<? super T>c :目标集合

        T...elements :要添加的元素(可变参)

       

泛型中,我们常见三种形式:

1、T                        具体类型参数

2、?extends T      上界通配符,表示T或T的子类型

3、?super T          下界通配符,表示T或T的父类型

                        

返回值:

        boolean,如果集合被修改则返回true

作用:

        将多个元素一次性添加到集合中

是否改变原始值:

        是,会修改目标集合

源码:

public static <T> boolean addAll(Collection<? super T> c, T... elements) {
    boolean result = false;
    for (T element : elements)
        result |= c.add(element);
    return result;
}

源码解读:

  • 使用可变参 T...elements 接收任意数量的元素
  • 遍历所有元素,逐个添加到集合中
  • 使用 位或运算 |= 累计添加结果,只要有元素成功添加就返回true

示例:

import java.util.*;

public class AddAllExample {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();
        
        // 使用 Collections.addAll 批量添加元素
        Collections.addAll(fruits, "apple", "banana", "orange", "grape");
        System.out.println("水果列表: " + fruits);
        // 输出: 水果列表: [apple, banana, orange, grape]
        
        // 对比传统方式
        List<String> vegetables = new ArrayList<>();
        vegetables.add("carrot");
        vegetables.add("potato");
        vegetables.add("tomato");
        System.out.println("蔬菜列表: " + vegetables);
        // 输出: 蔬菜列表: [carrot, potato, tomato]
    }
}

2、sort

方法声明:

        //自然排序版本

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

        //比较器排序版本

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

调用者:

        Collections

参数:

        List<T> list:要排序的列表

        Comparator<?super T>c:比较器(可选)

返回值:

        void

作用:

        对列表进行排序

是否改变原始值:

        是,会修改原列表

注意事项:

        自然排序要求实现 Comparable 接口

源码:

// 自然排序版本
public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null);
}

// 比较器排序版本
public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}

源码解读:

实际上是委托给 List 接口的 sort 方法实现

JDK 8 之后,List 接口增加了默认方法 sort,Collections.sort 成为了该方法的包装

JDK 8 之前 Collections.sort() 实现:

// JDK 8 之前的 Collections.sort() 实现(简化版)
public static <T extends Comparable<? super T>> void sort(List<T> list) {
    Object[] a = list.toArray();
    Arrays.sort(a);
    ListIterator<T> i = list.listIterator();
    for (int j=0; j<a.length; j++) {
        i.next();
        i.set((T)a[j]);
    }
}

JDK 8 之后 List 接口新增的默认方法:

// List 接口中的默认方法 sort()
default void sort(Comparator<? super E> c) {
    Object[] a = this.toArray();
    Arrays.sort(a, (Comparator) c);
    ListIterator<E> i = this.listIterator();
    for (int j=0; j<a.length; j++) {
        i.next();
        i.set((E) a[j]);
    }
}

JDK 8 之后 Collections.sort() 实现:

// JDK 8 之后 Collections.sort() 的实现(简化版)
public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null); // 委托给 List.sort() 方法
}

public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c); // 委托给 List.sort() 方法
}

总结一下:

  1. 功能转移:排序核心功能被转移到了 List 接口中
  2. 实现委托:Collections.sort() 方法现在内部调用 List.sort() 来实现排序功能

示例:

import java.util.*;

public class SortExample {
    public static void main(String[] args) {
        // 自然排序示例
        List<Integer> numbers = new ArrayList<>();
        Collections.addAll(numbers, 5, 2, 8, 1, 9);
        System.out.println("排序前: " + numbers);
        
        Collections.sort(numbers); // 自然排序
        System.out.println("自然排序后: " + numbers);
        // 输出: 自然排序后: [1, 2, 5, 8, 9]
        
        // 比较器排序示例(降序)
        List<String> words = new ArrayList<>();
        Collections.addAll(words, "banana", "apple", "cherry");
        System.out.println("排序前: " + words);
        
        Collections.sort(words, Collections.reverseOrder()); // 降序排序
        System.out.println("降序排序后: " + words);
        // 输出: 降序排序后: [cherry, banana, apple]
    }
}

3、reverse

方法声明:

        public static void reverse(List<?> list)

调用者:

        Collections

参数:

        List<?> list :要反转的列表 

返回值:

        void

作用:

        反转集合中的元素顺序

是否改变原始值:

        是,会修改原集合

注意事项:

        只适用于 List 类型的集合

源码:

public static void reverse(List<?> list) {
    int size = list.size();
    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 {
        // 对于大列表且非随机访问,使用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);
        }
    }
}

源码解读:

  • 根据列表大小和是否实现 RandomAccess 接口选择不同的实现策略
  • 小列表或者随机访问列表采用直接索引交换的方式
  • 大列表且非随机访问采用 LIstIterator

示例:

import java.util.*;

public class ReverseExample {
    public static void main(String[] args) {
        List<String> colors = new ArrayList<>();
        Collections.addAll(colors, "red", "green", "blue", "yellow");
        System.out.println("反转前: " + colors);
        // 输出: 反转前: [red, green, blue, yellow]
        
        Collections.reverse(colors);
        System.out.println("反转后: " + colors);
        // 输出: 反转后: [yellow, blue, green, red]
    }
}

4、shuffle

方法声明:

        public static void shuffle(List<?> list)

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

调用者:

        Collections

参数:

        List<?> list:要打乱的集合

        Random rnd:随机数生成器(可选)   

返回值:

        void

作用:

        随机打乱列表中的元素顺序

是否改变原始值:

        是,会修改原集合

注意事项:

        可以传入自定义的Random对象以控制随机种子

源码:

public static void shuffle(List<?> list) {
    Random rnd = r;
    if (rnd == null)
        r = rnd = new Random();
    shuffle(list, rnd);
}

public static void shuffle(List<?> list, Random rnd) {
    int size = list.size();
    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]);
        }
    }
}

源码解读:

  • 使用 Fisher-Yates 洗牌算法实现随机打乱
  • 根据列表大小和是否实现 RandomAccess 接口选择不同的实现策略

示例:

import java.util.*;

public class ShuffleExample {
    public static void main(String[] args) {
        List<Integer> cards = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            cards.add(i);
        }
        System.out.println("洗牌前: " + cards);
        
        Collections.shuffle(cards);
        System.out.println("洗牌后: " + cards);
        
        // 使用固定种子的随机数生成器
        Collections.shuffle(cards, new Random(123));
        System.out.println("固定种子洗牌: " + cards);
    }
}

5、max/min

方法声明:

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

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

调用者:

        Collections

参数:

        Collection<? extends T> coll:要查找的集合

        Comparator<? super T> comp:比较器(可选)

返回值:

        T,集合中的最大或最小元素

作用:

        查找集合中的最大或最小元素

是否改变原始值:

        否,不修改原集合

注意事项:

        自然排序要求实现 Comparable 接口

        空集合会抛出 NoSuchElementException

源码:

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();
        if (next.compareTo(candidate) < 0)
            candidate = next;
    }
    return candidate;
}

public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp) {
    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();
        if (comp.compare(next, candidate) < 0)
            candidate = next;
    }
    return candidate;
}

源码解读:

  • 通过迭代器遍历集合,比较元素大小
  • 自然排序使用 compareTo 方法,比较器排序使用 compare 方法

示例:

import java.util.*;

public class MaxMinExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        Collections.addAll(numbers, 10, 5, 8, 3, 15, 7);
        
        Integer max = Collections.max(numbers);
        Integer min = Collections.min(numbers);
        
        System.out.println("数字列表: " + numbers);
        System.out.println("最大值: " + max); // 输出: 最大值: 15
        System.out.println("最小值: " + min); // 输出: 最小值: 3
        
        // 使用自定义比较器(按绝对值比较)
        List<Integer> negativeNumbers = new ArrayList<>();
        Collections.addAll(negativeNumbers, -10, 5, -8, 3);
        
        // 按绝对值找最大值
        Integer maxAbs = Collections.max(negativeNumbers, Comparator.comparing(Math::abs));
        System.out.println("绝对值最大: " + maxAbs); // 输出: 绝对值最大: -10
    }
}

6、fill

方法声明:

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

调用者:

        Collections

参数:

        List<? super T> list:要填充的集合

        T obj:用于填充的对象

返回值:

        void

作用:

        使用指定的元素替换集合中的所有元素

是否改变原始值:

        是,会修改原集合

注意事项:

        集合必须已经有元素,不能是空列表

源码:

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

    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);
        }
    }
}

源码解读:

  • 根据列表大小和是否实现 RandomAccess 接口选择不同的实现策略
  • 使用索引或迭代器将所有元素替换为指定对象

示例:

import java.util.*;

public class FillExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList(new String[5]));
        System.out.println("填充前: " + list);
        
        Collections.fill(list, "default");
        System.out.println("填充后: " + list);
        // 输出: 填充后: [default, default, default, default, default]
        
        // 也可以用于初始化
        List<Integer> numbers = new ArrayList<>(Collections.nCopies(5, 0));
        System.out.println("初始化: " + numbers);
        Collections.fill(numbers, 1);
        System.out.println("填充为1: " + numbers);
    }
}

7、binarySearch

方法声明:

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

        private static <T> int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key)

调用者:

        Collections

参数:

        List<? extends Comparable<? super T>> list:已降序的集合

        T key:要查找的键

        Comparator<? super T> c:比较器(可选) 

返回值:

        int,找到则返回索引,未找到则返回负数

作用:

        在已排序集合中查找指定元素

是否改变原始值:

        否,不修改原集合

注意事项:

        集合必须已经排序

        返回值为负数时表示未找到,其绝对值减 1 是应该插入的位置

源码:

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);
        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
}

源码解读:

  • 实现标准的二分查找算法
  • 根据列表是否实现 RandomAccess 接口选择不同的实现策略

示例:

import java.util.*;

public class BinarySearchExample {
    public static void main(String[] args) {
        List<Integer> sortedList = new ArrayList<>();
        Collections.addAll(sortedList, 1, 3, 5, 7, 9, 11, 13);
        
        int index = Collections.binarySearch(sortedList, 7);
        System.out.println("元素7的索引: " + index); // 输出: 元素7的索引: 3
        
        int notFoundIndex = Collections.binarySearch(sortedList, 6);
        System.out.println("元素6的索引: " + notFoundIndex); // 输出: 元素6的索引: -4
        System.out.println("应该插入的位置: " + (Math.abs(notFoundIndex) - 1)); // 输出: 应该插入的位置: 3
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到