Objects / Arrays / Collections / Optional 工具类源码
概述
Objects、Arrays、Collections、Optional 是 java.util 包中四个"非容器"的工具类:它们不直接存储数据,而是为对象比较、数组操作、集合操作和空值表达提供统一的静态方法与类型。
Objects:null 安全的比较、哈希、非空校验与索引检查,是 JDK 内部使用频率最高的工具类之一。Arrays:数组排序、二分查找、复制、填充与asList视图,排序核心委托DualPivotQuicksort与ComparableTimSort。Collections:排序、二分查找、同步包装、不可变包装、类型安全包装等集合静态工厂。Optional:容器式空值封装,用值的存在性表达替代null判断。
本文基于 OpenJDK 21 源码,先拆解 Objects 的 null-safe 工具方法,再深入 Arrays 与 Collections 的排序与包装实现,最后完整梳理 Optional 的构造、取值与链式操作。
核心源码解析
① Objects.equals(Object a, Object b) 的 null-safe
java
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}- 先做引用比较短路
a == b:两引用相同(含都为null)直接返回true,避免后续调用。 - 再判断
a != null,彻底规避a.equals(b)的空指针风险,等价于把a == null ? b == null : a.equals(b)写成一条表达式。 - 数组参数时
equals退化为引用比较(数组不重写equals),深层比较需用deepEquals:
java
public static boolean deepEquals(Object a, Object b) {
if (a == b) return true;
else if (a == null || b == null) return false;
else return Arrays.deepEquals0(a, b); // 递归处理数组
}deepEquals0 对 Object[] 递归调用 deepEquals,对 8 种原始类型数组(int[]、long[] 等)逐一 Arrays.equals 比较元素,实现"任意维度数组的内容相等"。
② Objects.hash(Object... values) 的变参哈希
java
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0; // null 哈希为 0
}
public static int hash(Object... values) {
return Arrays.hashCode(values);
}hash 直接委托 Arrays.hashCode(Object[]),而 Arrays.hashCode 用的是经典的 31 加权规则:
java
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}Objects.hash(a, b, c)等价于31 * (31 * (31 * 1 + h(a)) + h(b)) + h(c),配合31质数加权降低碰撞。- 因为
hash接收变参,任何对象数组作为实参时必须手动Arrays.hashCode或套一层数组,否则会被展开为多个元素参与计算。 - 常用于在
equals中配对实现hashCode:return Objects.hash(field1, field2);。
③ Objects.requireNonNull(T obj) 的快速失败
java
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
public static <T> T requireNonNull(T obj, String message) {
if (obj == null)
throw new NullPointerException(message);
return obj;
}- 快速失败(fail-fast):在方法入口即校验并抛出
NullPointerException,把空指针错误提前到赋值现场,而不是延迟到后续某个调用点。 - 返回
obj本身,支持内联使用:String s = Objects.requireNonNull(getName(), "name 不能为空");。 - 配套的默认值方法:
requireNonNullElse(T obj, T defaultObj)与requireNonNullElseGet(T obj, Supplier)在obj == null时返回默认值(默认值本身被requireNonNull校验)。 - JDK 9+ 扩展的索引校验族
checkIndex(int index, int length)/checkFromIndexSize/checkFromToIndex由Preconditions支持,越界抛IndexOutOfBoundsException并生成带具体数值的异常消息,是List.get等边界校验的公共实现。
④ Arrays.sort(int[]) 的 DualPivotQuicksort
java
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, 0, a.length);
}DualPivotQuicksort 按数组长度在三种策略间切换:
| 长度区间 | 策略 | 说明 |
|---|---|---|
< INSERTION_SORT_THRESHOLD(47) | 插入排序 | 近乎有序时 O(n) |
< QUICKSORT_THRESHOLD(286) | 双轴快排 | 选取两个枢轴(pivot1 < pivot2),一趟划分为三段,O(n log n) |
| ≥ 286 | 归并排序 / 双轴快排 | 先统计升/降序 run:run 数过少(MAX_RUN_COUNT 67 次内耗尽)说明数据接近有序,改走归并排序;否则双轴快排 |
双轴快排一趟划分把区间分成"小于 pivot1、介于两者之间、大于 pivot2"三块,相比单轴快排每次递归多划出一段,常数更优;随机数组场景通常比 JDK 7 之前的单轴实现快。
⑤ Arrays.sort(Object[]) 的 ComparableTimSort
java
public static void sort(Object[] a) {
ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}ComparableTimSort(无自定义比较器的 TimSort)核心流程:
java
static void sort(Object[] a, int lo, int hi, ...) {
int nRemaining = hi - lo;
if (nRemaining < 2) return; // 1 个元素无需排序
if (nRemaining < MIN_MERGE /*32*/) {
int initRunLen = countRunAndMakeAscending(a, lo, hi); // 找天然 run
binarySort(a, lo, hi, lo + initRunLen); // 小数组二分插入排序
return;
}
// 大数组:找 run → 压栈 → 归并
ComparableTimSort ts = new ComparableTimSort(a, null, 0, 0);
int minRun = minRunLength(nRemaining);
do {
int runLen = countRunAndMakeAscending(a, lo, hi);
if (runLen < minRun) binarySort(...); // run 太短用二分插入补齐
ts.pushRun(lo, runLen);
ts.mergeCollapse(); // 满足栈不变量即归并
lo += runLen;
nRemaining -= runLen;
} while (nRemaining != 0);
ts.mergeForceCollapse();
}TimSort 的关键在归并阶段:
java
private Object[] mergeLo(Object[] baseA, int lenA, Object[] baseB, int lenB) {
// 二分定位,跳过不可能参与归并的前缀/后缀
int k = gallopRight(baseA[0], baseB, 0, lenB, 0); // 在 B 中定位 A[0]
...
// 成组移动:gallop 模式下一次移动多个元素,减少比较次数
outer:
while (true) {
int len1 = runHi - runLo; // 模式切换阈值 MIN_GALLOP=7
...
do {
if (compare(baseA[cursor1], baseB[cursor2]) <= 0) { ... }
else { ... }
} while (... < MIN_GALLOP);
// 进入 gallop 模式:连续赢 7 次后改为二分找位置、成块 System.arraycopy
int k1 = gallopRight(...);
int k2 = gallopLeft(...);
}
}gallopLeft/gallopRight:在已排序子序列中指数步长(1、2、4、8...)二分探测插入位置,一次跳过多余的比较;常规归并在数据倾斜时会产生大量无效比较,gallop 正是为对抗这种"一个 run 的元素几乎都小于另一个 run"的情况。- 稳定排序:
mergeLo/mergeHi用System.arraycopy成块搬移,相等元素保持原始顺序,这是对象排序必须稳定(Collections.sort契约)的原因。
⑥ Arrays.asList(T...) 的 ArrayList 包装
java
@SafeVarargs
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable {
private final E[] a; // 直接引用传入数组
ArrayList(E[] array) { a = Objects.requireNonNull(array); }
public E get(int index) { return a[index]; }
public E set(int index, E element) { ... return oldValue; }
}- 返回的
ArrayList是Arrays的私有静态内部类,与java.util.ArrayList无关:不复制数组,而是直接持有原数组引用,set的修改会同步反映到原数组。 - 由于没有
add/remove,结构修改继承AbstractList抛UnsupportedOperationException,也不支持扩容——它本质是"数组的List视图"。
⑦ Collections.binarySearch(List, key) 的二分查找
java
public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) {
...
if (list instanceof RandomAccess || list.size() < BINARYSEARCH_THRESHOLD /*5000*/)
return indexedBinarySearch(list, key, c); // 支持随机访问 → 按索引折半
else
return iteratorBinarySearch(list, key, c); // 链表 → 迭代器折半
}indexedBinarySearch:low/high双指针,mid = (low + high) >>> 1(无符号右移防溢出),list.get(mid)取值比较,O(log n)。iteratorBinarySearch:链表无 O(1) 下标访问,退化为迭代器逐个推进,虽然仍是二分次数但每次移动需 O(mid),整体 O(n log n)。- 未命中时返回
-(low + 1)即插入点,供add定位使用。
⑧ Collections.unmodifiableList(List) 的装饰器
java
public static <T> List<T> unmodifiableList(List<? extends T> list) {
return (list instanceof RandomAccess ?
new UnmodifiableRandomAccessList<>(list) :
new UnmodifiableList<>(list));
}
static class UnmodifiableList<E> extends UnmodifiableCollection<E> implements List<E> {
final List<? extends E> list; // 持有原列表
public E get(int index) { return list.get(index); }
public void add(int index, E element) { throw new UnsupportedOperationException(); }
public E remove(int index) { throw new UnsupportedOperationException(); }
public E set(int index, E element) { throw new UnsupportedOperationException(); }
// 所有结构修改方法全部抛 UnsupportedOperationException
}- 装饰器模式:包装原列表,只透传读操作,把所有写操作(
add/remove/set/clear等)改为抛异常。 - 注意是浅层视图:原列表变化会反映到不可变视图;且视图只保证"不可通过该视图修改",原引用仍可改。
- JDK 9+ 的
List.of(...)是另一套真正不可变的不可变集合(ImmutableCollections),二者设计不同。
⑨ Collections.synchronizedList(List) 的同步包装
java
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
static class SynchronizedCollection<E> implements Collection<E> {
final Collection<E> c; // 被包装集合
final Object mutex; // 同步锁
SynchronizedCollection(Collection<E> c) {
this.c = Objects.requireNonNull(c);
mutex = this; // 默认以包装器自身为锁
}
public int size() { synchronized (mutex) { return c.size(); } }
public boolean add(E e) { synchronized (mutex) { return c.add(e); } }
}- 每个方法整体
synchronized (mutex),mutex默认是包装对象自身,也可通过双参构造指定外部锁对象,实现"多个包装共享同一把锁"。 - 迭代器不参与加锁:
iterator()本身没有同步,并发迭代仍需外部synchronized (list)包裹,这是官方文档明确标注的注意点。
⑩ Optional.of(T) / ofNullable(T) / empty()
java
public final class Optional<T> {
private static final Optional<?> EMPTY = new Optional<>(); // 空值单例
private final T value; // 只允许为 null 或非 null 两种状态
private Optional() { this.value = null; } // 空构造
private Optional(T value) { this.value = Objects.requireNonNull(value); } // 拒 null
public static <T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY; // 返回共享单例
return t;
}
public static <T> Optional<T> of(T value) {
return new Optional<>(value); // value 为 null 抛 NPE
}
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value); // 允许 null → 空 Optional
}
}EMPTY单例:所有empty()与ofNullable(null)返回同一个预创建实例,避免为"空"重复分配对象。of拒绝 null:走私有构造Objects.requireNonNull(value),语义是"有值容器"。Optional只有一个final T value字段(可能为 null),用"value 是否为 null"区分空与非空,无额外状态标志。get()在value == null时抛NoSuchElementException;isPresent()/isEmpty()直接判value != null。
⑪ Optional.ifPresent(Consumer) / orElse(T other)
java
public void ifPresent(Consumer<? super T> action) {
if (value != null)
action.accept(value);
}
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
if (value != null)
action.accept(value);
else
emptyAction.run();
}
public T orElse(T other) {
return value != null ? value : other;
}
public T orElseGet(Supplier<? extends T> supplier) {
return value != null ? value : supplier.get();
}
public T orElseThrow() { // JDK 10 无参版本
T value = this.value;
if (value == null)
throw new NoSuchElementException("No value present");
return value;
}- 消费:
ifPresent仅在非空时执行Consumer;ifPresentOrElse同时给出空分支回调。 - 取回:
orElse直接给默认值(参数总是被求值),orElseGet惰性获取默认值(只在为空时调用Supplier)——默认值构造代价高时应选orElseGet。 - 抛错:
orElseThrow()抛标准NoSuchElementException;带参版本orElseThrow(Supplier)抛自定义异常,且空分支不会执行任何返回逻辑。
⑫ Optional.map(Function) / flatMap(Function) 的链式
java
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper); // 方法引用不允许为 null
if (!isPresent())
return empty(); // 空值传播:返回空
else
return Optional.ofNullable(mapper.apply(value)); // 结果自动包装
}
public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
@SuppressWarnings("unchecked")
Optional<U> r = (Optional<U>) mapper.apply(value); // 结果必须是 Optional
return Objects.requireNonNull(r); // 返回的 Optional 本身不为 null
}
}map自动包装:映射结果经ofNullable自动包成Optional(结果非 null →of,结果 null →empty),所以map的Function返回裸值即可,形成Optional<A> → Optional<B>的映射链。flatMap不自动包装:Function必须返回Optional,flatMap直接透传(同时用requireNonNull校验返回的Optional不为 null),用于扁平化——避免链式调用产生Optional<Optional<T>>的嵌套。filter(Predicate)在谓词不满足时返回empty();or(Supplier)(JDK 9)在自身为空时用供给的Optional兜底;stream()(JDK 9)把有值转成单元素流、空值转成空流,便于与Stream管线衔接。
总结
这四个工具类覆盖了日常编码的高频空值与容器操作:
| 类 | 职责 | 关键实现 |
|---|---|---|
Objects | null-safe 工具 | equals 短路 + hash 委托 Arrays.hashCode 31 加权、requireNonNull 快速失败、checkIndex 索引校验 |
Arrays | 数组工具 | DualPivotQuicksort 三阈值切换、ComparableTimSort 二分插入 + gallop 归并、asList 固定数组视图 |
Collections | 集合工具 | binarySearch 索引/迭代器双实现、unmodifiableList / synchronizedList 装饰器包装 |
Optional | 空值容器 | EMPTY 单例 + 三工厂、map 自动包装与 flatMap 扁平化、orElseGet 惰性默认值 |
设计上它们都遵循"静态工具 + 委托"的模式:Objects 委托 Arrays 完成数组哈希,Collections 委托 Arrays 完成排序,Optional 以 Objects.requireNonNull 作为非空约束——工具类之间通过委托复用核心算法,自身保持极薄的封装层。