当前位置: 首页 > news >正文

Collections 工具类 15 个常用方法源码:sort、binarySearch、reverse、shuffle、unmodifiableXxx

关键词:Collections 工具类、sort、binarySearch、reverse、shuffle、unmodifiableXxx、TimSort、源码、面试
适合人群:Java 初中高级工程师 · 面试冲刺 · 代码调优 · 架构设计
阅读时长:35 min(≈ 5500 字)
版本环境:JDK 17(源码行号对应 jdk-17+35)


1. 开场白:面试四连击,答不出就挂

  1. “Collections.sort 用的是哪种排序算法?时间复杂度多少?”
  2. “binarySearch 对未排序列表返回什么值?怎么计算插入点?”
  3. “shuffle 算法公平吗?如何自定义随机源?”
  4. “unmodifiableList 返回的 List 能新增元素吗?为什么不能?”

阿里 P7 面完 100 人,能把 TimSort 归并栈、洗牌 Fisher-Yates、代理视图陷阱说透的不超过 5 个。
线上事故:某配置中心用 Collections.unmodifiableList() 包装后暴露给业务,业务反射新增元素,导致 UnsupportedOperationException 线上狂飙 5k 次,网关熔断。
背完本篇,你能手写 TimSort 归并栈、复现 Java 版洗牌、给出 3 种只读视图方案,让面试官闭嘴。


2. 知识骨架:Collections 15 核表一网打尽

方法族代表方法算法/实现时间复杂度备注
sortsort(List<T> list)TimSortO(n log n)稳定
binarySearchbinarySearch(List<? extends Comparable> list, T key)二分O(log n)需有序
reversereverse(List<?> list)双指针交换O(n)原位
shuffleshuffle(List<?> list)Fisher-YatesO(n)Random 实例
unmodifiableXxxunmodifiableList(List<? extends T> list)代理视图读 O(1)写抛异常
synchronizedXxxsynchronizedMap(Map<K,V> m)全对象锁读写串行已废弃趋势
emptyXxxemptyList()单例空集O(1)全局不可变
singletonXxxsingleton(T o)单元素集O(1)迭代一次

3. 身世档案:核心常量与入口方法

常量/字段含义值/备注
DEFAULT_THRESHOLDTimSort 小数组归并阈值32
MIN_MERGETimSort 最小归并长度16
UNMODIFIABLE代理标记私有静态类

4. 原理解码:源码逐行,行号指路

4.1 sort() → TimSort 入口(行号 234)

@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> void sort(List<T> list) {Object[] a = list.toArray();                    // ① 转数组Arrays.sort(a);                                 // ② TimSortListIterator<T> i = list.listIterator();for (int j=0; j<a.length; j++) {i.next();i.set((T)a[j]);                             // ③ 写回原 list}
}

稳定排序;ArrayList 走 Arrays.sort((T[]) a) 免拷贝。

Arrays.sort(int[]) 快排 + 双轴(行号 1482)
static void sort(int[] a, int left, int right) {// 双轴快排 DualPivotQuicksort
}

原生类型用双轴快排;对象类型用 TimSort。

TimSort 关键逻辑(行号 389)
private static <T> void mergeSort(T[] a, T[] aux, int lo, int hi, Comparator<? super T> c) {if (hi - lo < MIN_MERGE) {binarySort(a, lo, hi, lo, c);               // 二分插入return;}int mid = (lo + hi) >>> 1;mergeSort(a, aux, lo, mid, c);mergeSort(a, aux, mid, hi, c);if (c.compare(a[mid-1], a[mid]) <= 0) return;   // 已有序免归并merge(a, aux, lo, mid, hi, c);                  // 归并
}

4.2 binarySearch() 二分 + 插入点(行号 2729)

public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) {if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)return indexedBinarySearch(list, key);      // 索引访问elsereturn iteratorBinarySearch(list, key);     // 迭代器访问
}

返回值:< 0 表示 -(插入点) - 1

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;}return -(low + 1);                              // 插入点
}

4.3 reverse() 双指针交换(行号 2437)

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

REVERSE_THRESHOLD = 256;RandomAccess 用索引,否则用迭代器对称交换。

4.4 shuffle() Fisher-Yates(行号 2671)

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 (Object e : arr) {it.next();it.set(e);}}
}

公平性:每个排列概率 1/n!;可传入 new Random(seed) 复现序列。

4.5 unmodifiableList() 代理视图(行号 1324)

public static <T> List<T> unmodifiableList(List<? extends T> list) {return (list instanceof RandomAccess ?new UnmodifiableRandomAccessList<>(list) :new UnmodifiableList<>(list));
}
UnmodifiableList 典型方法(行号 1276)
public boolean add(E e) {throw new UnsupportedOperationException();  // 只读
}

代理模式:所有写方法直接抛异常;读方法转发给底层 list。


5. 实战复现:4 段代码 + 坑位演示

5.1 sort 对自定义对象排序

List<Student> list = Arrays.asList(new Student("Tom", 20), new Student("Alice", 18));
Collections.sort(list, Comparator.comparingInt(Student::getAge));
System.out.println(list); // [Alice(18), Tom(20)]

5.2 binarySearch 获取插入点

List<Integer> sorted = Arrays.asList(1, 3, 5, 7);
int idx = Collections.binarySearch(sorted, 6); // 返回 -4
int insert = -(idx + 1);                       // 3
sorted.add(insert, 6);                         // [1, 3, 5, 6, 7]

5.3 shuffle 公平性验证

List<Integer> list = IntStream.rangeClosed(1, 5).boxed().collect(Collectors.toList());
Collections.shuffle(list, new Random(42));
System.out.println(list); // 相同种子可重复

5.4 unmodifiableList 反射陷阱

List<String> inner = new ArrayList<>();
inner.add("A");
List<String> outer = Collections.unmodifiableList(inner);
inner.add("B");                 // 底层 list 变化
System.out.println(outer);      // [A, B] — 视图实时可见
outer.add("C");                 // UnsupportedOperationException

只读代理禁止通过 outer 写,但底层 inner 仍可变;真正不可变需 List.copyOf() 或 Guava ImmutableList


6. 线上事故:shuffle 随机源重复导致奖品分布偏差

背景
抽奖系统用 Collections.shuffle(list) 发奖,未指定 seed,压测时复现同一序列。

现象
同一用户多次抽到同一奖品,被投诉“暗箱操作”。

根因
new Random() 种子相同(系统时间粒度粗),shuffle 序列重复。

复盘

  1. 修复:使用 SecureRandom 并每轮重新 seed。
  2. 防呆:
    • 线上 shuffle 必须 SecureRandom
    • 单元测试断言分布均匀(卡方校验)。

7. 面试 10 连击:答案 + 行号

问题答案
1. Collections.sort 用哪种算法?TimSort(行号 234)
2. 时间复杂度?O(n log n) 稳定
3. binarySearch 返回值含义?≥0 索引;<0 插入点 -(idx+1)(行号 2737)
4. reverse 对链表怎么实现?双向迭代器对称交换(行号 2445)
5. shuffle 公平性?Fisher-Yates,每种排列 1/n!(行号 2673)
6. unmodifiableList 能否新增?不能,抛 UnsupportedOperationException(行号 1276)
7. 底层 list 修改后视图可见吗?可见,代理实时转发
8. 如何真正不可变?List.copyOf() 或 Guava ImmutableList
9. synchronizedMap 锁粒度?全对象 mutex(行号 1685)
10. sort 对 ArrayList 优化?直接 Arrays.sort((T[]) a) 免拷贝(行号 240)

8. 总结升华:一张脑图 + 三句话口诀

[脑图文字版]
中央:Collections 工具
├─sort:TimSort 稳定 n log n
├─binarySearch:插值 -(idx+1)
├─reverse:双指针交换
├─shuffle:Fisher-Yates
└─unmodifiable:代理只读

口诀:
“Tim 归并稳定快,二分负值插位来;洗牌公平加种子,只读代理底层开。”


9. 下篇预告

阶段 3 第一炮《ConcurrentHashMap 1.7 与 1.8 源码对比:分段锁 → CAS + synchronized》将带你手绘分段锁内存图、CAS 无锁化、sizeCtl 魔法,敬请期待!


10. 互动专区

你在生产环境踩过 shuffle 随机源或 unmodifiable 视图坑吗?评论区贴出异常堆栈 / 分布图,一起源码级排查!

http://www.dtcms.com/a/499754.html

相关文章:

  • mb与使用场景
  • 建设通网站是什么时间成立加入google广告wordpress
  • AI Coding 基础实践01 - TickTalk的MarsCode-Trae AI(Trae 插件)在Pycharm中的配置
  • [SCADE编译原理] 因果性分析原理(2001)
  • 网站建设pc指什么软件佛山新网站建设策划
  • RDEx:一种效果驱动的混合单目标优化器,自适应选择与融合多种算子与策略
  • JavaScript学习第三天:运算符
  • C++进阶之操作符重载函数operator[]:用法实例(四百三十五)
  • 《小白学随机过程》第一章:随机过程——定义和形式(附录2. 随机变量和随机过程公式解读)
  • 近代通信技术的发展
  • 实用网站的设计与实现wordpress简介
  • 如何微信做演讲视频网站Wordpress刷新CDN缓存
  • macos虚拟机-演示篇一制作可启动iso文件
  • 论坛类网站备案今天东营发生的重大新闻
  • Aspect的AOP实现
  • Orleans Stream SubscriptionId 生成机制详解
  • FMIT,一款专业的乐器调音助手
  • 医疗器械招商网站大全建一个信息 类网站
  • 不用域名推广网站开源网站后台管理系统
  • 欧司朗与日亚签署广泛的知识产权协议
  • Kotlin 与 Java 互操作中常用注解
  • 计算机操作系统:程序的装入与链接
  • 怎么建设网站手机网站制作价格在线考试类网站怎么做
  • 3.1 栈
  • 国贸网站建设公司服务专业的网站建站公司
  • 手写观察者模式:原理、实现与应用
  • 商贸有限公司网站案例wordpress模版安装
  • Rclone、rsync、Docker 的 COPY/ADD:路径末尾加不加「/」的含义大不相同!
  • 【复习】计网每日一题1016--可分配
  • 【STM32】hal库 多通道ADC(+DMA)采集 连续模式