前言
早期开发者经常需要对集合进行各种操作
比如排序、查找最大最小值等等
但是当时没有统一的工具类来处理
所以导致代码重复且容易出错
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() 方法 }
总结一下:
- 功能转移:排序核心功能被转移到了 List 接口中
- 实现委托: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 {// 对于大列表且非随机访问,使用ListIteratorListIterator 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); // 输出: 最大值: 15System.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);elsereturn 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;elsereturn 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的索引: 3int notFoundIndex = Collections.binarySearch(sortedList, 6);System.out.println("元素6的索引: " + notFoundIndex); // 输出: 元素6的索引: -4System.out.println("应该插入的位置: " + (Math.abs(notFoundIndex) - 1)); // 输出: 应该插入的位置: 3}
}