Collections和Arrays工具类整理

一、核心认知类考点

1、Collections 和 Arrays 工具类的核心特点?

  1. 两者均位于 java.util 包下,且都是不可实例化的工具类(构造方法私有,所有方法为 static);
  2. Collections 专注于操作 / 扩展 Collection 接口(List/Set/Queue)及其实现类;
  3. Arrays 专注于数组的操作(排序、查找、转换、填充等),同时提供数组与集合的互转方法。

2、为什么这两个类不能被实例化?

2.1、工具类的设计目标是提供静态方法,无需创建实例;

2.2、源码层面:构造方法被声明为 private 且抛出异常,防止通过反射实例化;

java 复制代码
// Collections 源码示例
private Collections() {
    throw new UnsupportedOperationException();
}

2.3、符合 Java 工具类的设计规范(如 Math 类同理)

3、核心功能对比

维度 Collections 工具类 Arrays 工具类
操作对象 List/Set/Queue 等集合 各种类型的数组(基本类型 + 引用类型)
核心功能 排序、查找、同步化、不可变集合、批量添加等 排序、查找、填充、数组转集合、复制、比较等
线程安全 提供同步集合包装方法(如 synchronizedList) 无线程安全相关方法
空值处理 部分方法(如 sort)不支持 null 元素 支持数组中存在 null(如 sort 引用类型数组)

二、Collections 工具类

1、Collections与 Collection 接口的区别

概念 本质 核心作用
Collection 接口 定义集合的通用操作规范
Collections 工具类 提供操作 Collection 的静态方法
  • 面试陷阱:避免将「工具类 Collections」与「集合顶层接口 Collection」混淆,这是入门级易错点。

2、高频 API 考点

1. 排序 & 查找

核心方法
java 复制代码
List<Integer> list = new ArrayList<>(Arrays.asList(3, 1, 2));
// 1. 自然排序(元素需实现 Comparable 接口)
Collections.sort(list); // 结果:[1,2,3]
// 2. 自定义排序(Comparator 比较器)
Collections.sort(list, (a, b) -> b - a); // 降序:[3,2,1]
// 3. 二分查找(前提:List 已升序排序)
int index = Collections.binarySearch(list, 2); 
面试考点
  • 考点 1:binarySearch 返回值规则(重中之重):
    • 找到元素:返回元素索引;
    • 未找到元素:返回 -(插入点) - 1(插入点指 "元素应插入的位置",例如插入点为 0 → 返回 - 1,插入点为 2 → 返回 - 3);
  • 考点 2:sort 方法的异常场景:List 中包含 null 元素时,会抛出 NullPointerException
  • 延伸考点:手写实现 Collections.sort 的核心逻辑。

2、不可变集合

核心方法
java 复制代码
// 1. 空不可变集合(复用单例,无扩容开销)
List<String> emptyList = Collections.emptyList();
Set<Integer> emptySet = Collections.emptySet();
// 2. 单元素不可变集合
List<String> singletonList = Collections.singletonList("test");
// 3. 包装不可变集合(浅不可变)
List<String> originList = new ArrayList<>();
List<String> unmodifiableList = Collections.unmodifiableList(originList);
面试考点
  • 考点 1:三种不可变集合的区别:
    • emptyList()/singletonList():底层是私有静态内部类,无扩容 / 内存开销,效率最高;
    • unmodifiableList():仅为 "包装器",原集合修改会同步反映到包装集合中(浅不可变);
  • 考点 2:与 JDK 9+ List.of() 的对比(延伸题):
    • Collections.unmodifiableList():浅不可变,原集合修改会影响包装集合;
    • List.of():深不可变,创建后无法修改,且不允许 null 元素;
  • 考点 3:不可变集合的应用场景:常量定义、方法返回值(防止外部修改)、线程安全(只读场景)。

3、同步集合(线程安全考点)

核心方法
java 复制代码
// 将非线程安全集合包装为线程安全
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
Set<String> syncSet = Collections.synchronizedSet(new HashSet<>());
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());

考点 1:同步集合的局限性:

  • 仅实现「方法级同步」(方法加 synchronized),遍历集合时需手动加锁 ,否则可能抛出 ConcurrentModificationException

考点 2:与并发集合的对比(高频延伸题):

  • Collections.synchronizedList:性能低(全局锁),适合低并发场景;
  • CopyOnWriteArrayList(JUC 包):读写分离,适合读多写少场景,性能更高;

4. 其他高频方法

方法 作用 面试注意点
Collections.reverse(list) 反转集合元素 -
Collections.shuffle(list) 随机打乱元素(洗牌算法) 可用于抽奖 / 随机排序场景
Collections.swap(list, i, j) 交换指定位置元素 -
Collections.max(list) 获取最大值 元素需实现 Comparable 接口
Collections.addAll(list, e1, e2) 批量添加 比集合自身的 addAll 更高效
Collections.fill(list, obj) 填充元素 将集合所有元素替换为指定对象

3、面试高频问题和易错陷阱

1. 高频问答

Collections 提供的同步集合是真正的线程安全吗?
  • 答案:不完全是。方法级同步仅保证单个方法的原子性,复合操作(如 "检查 - 再操作"、遍历)需手动加锁,否则仍会出现线程安全问题。
为什么 Collections.emptyList () 比 new ArrayList () 更高效?
  • 答案:emptyList() 返回的是静态单例对象(底层无数组、无扩容),而 new ArrayList() 会初始化长度为 10 的数组,存在内存开销。
Collections.sort () 的底层实现?
  • 答案:底层调用 Arrays.sort(),先将 List 转为数组排序,再将排序后的数组写回 List;JDK 8 中,引用类型数组用 TimSort(归并 + 插入),基本类型用双轴快排。

2. 易错陷阱

  • 调用 unmodifiableList() 后修改原集合,认为包装集合不会变(实际会同步变化);
  • binarySearch 未排序就使用,认为返回 - 1 表示未找到(实际返回值规则复杂);
  • 遍历 synchronizedList 时不加锁,导致并发修改异常;
  • Collections.sort() 传入包含 null 的 List,未捕获 NPE 异常。

三、Arrays工具类

1、基础特性

考点 1:工具类的核心设计特点

  1. Arrays不可实例化 的静态工具类:构造方法被私有化(源码层面),防止创建对象,所有方法均为 static
  2. 操作对象:支持所有类型数组(基本类型数组:int []/char [] 等;引用类型数组:String []/Object [] 等);
  3. 核心定位:提供数组的排序、查找、复制、填充、比较、转集合等一站式操作,替代手动遍历实现。

考点 2:与数组原生操作的区别

操作场景 原生数组操作(for 循环) Arrays 工具类
排序 需手动实现排序算法 内置优化算法
数组比较 需遍历逐个对比 equals() 一键对比
数组转字符串 需遍历拼接 toString() 直接输出
数组复制 需手动遍历赋值 copyOf() 高效实现

Arrays 工具类的设计目标是简化数组操作、提升性能 (底层调用 native 方法如 System.arraycopy)。

2、高频 API 考点

1. 排序 & 查找

核心方法
java 复制代码
int[] arr = {3, 1, 2, 5, 4};
// 1. 全数组排序(默认升序)
Arrays.sort(arr); // 结果:[1,2,3,4,5]
// 2. 范围排序(左闭右开区间)
Arrays.sort(arr, 0, 3); // 仅排序[0,3):[1,2,3,5,4]
// 3. 二分查找(前提:数组已升序排序)
int index = Arrays.binarySearch(arr, 3); // 找到,返回2
int notFoundIndex = Arrays.binarySearch(arr, 6); // 未找到,返回-6
面试考点
  • 考点 1:排序算法的底层实现(重中之重):
    • 基本类型数组(int []/char [] 等):使用双轴快速排序(Dual-Pivot QuickSort),特点:效率高、不稳定;
    • 引用类型数组(String []/Object [] 等):使用TimSort(归并排序 + 插入排序),特点:稳定、适配真实场景的有序子序列;
  • 考点 2:binarySearch 返回值规则(与 Collections.binarySearch 一致):
    • 找到元素:返回元素索引;
    • 未找到元素:返回 -(插入点) - 1(插入点 = 元素应插入的位置,例如数组 [1,3,5] 查找 4,插入点为 2,返回 - 3);
  • 考点 3:sort 对 null 的支持:引用类型数组允许 null(null 视为最小元素),基本类型数组无 null 概念。

2. 数组转集合

核心方法
java 复制代码
String[] arr = {"a", "b", "c"};
// 数组转集合
List<String> list = Arrays.asList(arr);
面试考点
  • 🔥 考点 1:返回值类型陷阱:Arrays.asList() 返回的是 java.util.Arrays.ArrayList(Arrays 的私有静态内部类),不是 java.util.ArrayList
  • 🔥 考点 2:固定长度陷阱:该集合是固定长度 的,调用 add()/remove() 会抛出 UnsupportedOperationException
  • 🔥 考点 3:强引用绑定陷阱:
    • 数组与集合强绑定,修改数组元素会同步修改集合,反之亦然;
java 复制代码
arr[0] = "x";
System.out.println(list.get(0)); // 输出 x(集合被同步修改)
  • 🔥 考点 4:正确解决方案:
    • 转成真正的 ArrayList 以支持增删:
java 复制代码
List<String> realList = new ArrayList<>(Arrays.asList(arr));

3. 数组复制 & 填充

核心方法
java 复制代码
int[] arr = {1,2,3};
// 1. 全数组复制(扩容/缩容)
int[] copyArr1 = Arrays.copyOf(arr, 5); // 扩容到5,补0:[1,2,3,0,0]
int[] copyArr2 = Arrays.copyOf(arr, 2); // 缩容到2:[1,2]
// 2. 范围复制(左闭右开)
int[] rangeCopy = Arrays.copyOfRange(arr, 0, 2); // [1,2]
// 3. 全数组填充
Arrays.fill(arr, 0); // 全部填充0:[0,0,0]
// 4. 范围填充
Arrays.fill(arr, 0, 2, 1); // [0,2)填充1:[1,1,0]
面试考点
  • Arrays.copyOf() 底层实现:调用 System.arraycopy()(native 本地方法,基于内存块复制,比 for 循环高效);

  • System.arraycopy() 的区别:

    维度 Arrays.copyOf() System.arraycopy()
    返回值 返回新数组 无返回值(修改目标数组)
    灵活性 仅复制全数组 / 指定长度 可指定源 / 目标数组、起始位置
    底层 调用 System.arraycopy () 原生本地方法
  • 应用场景:ArrayList 底层扩容就是调用 Arrays.copyOf() 实现。

4. 数组比较 & 转字符串

核心方法
java 复制代码
int[] arr1 = {1,2,3};
int[] arr2 = {1,2,3};
int[][] deepArr1 = {{1},{2}};
int[][] deepArr2 = {{1},{2}};

// 1. 一维数组比较(内容相等返回true)
boolean equals = Arrays.equals(arr1, arr2); // true
// 2. 多维数组比较
boolean deepEquals = Arrays.deepEquals(deepArr1, deepArr2); // true
// 3. 一维数组转字符串
String str = Arrays.toString(arr1); // "[1, 2, 3]"
// 4. 多维数组转字符串
String deepStr = Arrays.deepToString(deepArr1); // "[[1], [2]]"
面试考点
  • ==Arrays.equals() 的区别:
    • ==:比较数组的引用地址(是否为同一个数组);
    • Arrays.equals():比较数组的内容(每个元素是否相等);
  • 多维数组必须用 deepEquals()/deepToString():一维方法无法穿透数组层级,多维数组用 equals() 会比较数组地址而非内容。

3、面试高频问题 & 易错陷阱

1. 高频问答

Arrays.asList() 为什么返回的集合不能增删?
  • 答案:Arrays.ArrayList 未重写 add()/remove() 方法,直接继承自 AbstractList,这些方法默认抛出 UnsupportedOperationException
Arrays.sort() 对基本类型和引用类型排序的算法不同,为什么?
  • 答案:
    1. 基本类型无需考虑稳定性(无对象引用相等的问题),双轴快排效率更高;
    2. 引用类型需要排序稳定性(保证相等元素的相对位置不变),TimSort 更适配。
为什么 Arrays.binarySearch 要求数组升序排序?
  • 答案:二分查找的核心逻辑是基于 "有序数组的中间值对比",无序数组会导致中间值判断失效,返回结果不可预测。

2. 易错陷阱(避坑指南)

  • Arrays.asList() 处理基本类型数组,导致集合仅包含一个数组元素;
  • 认为 Arrays.asList() 返回的是 java.util.ArrayList,调用 add() 抛异常;
  • 多维数组用 equals()/toString() 替代 deepEquals()/deepToString()
  • 未排序就调用 binarySearch,认为返回 - 1 表示未找到;
  • 混淆 Arrays.copyOf()System.arraycopy() 的使用场景。
相关推荐
golang学习记2 小时前
[特殊字符] Go Gin 不停机重启指南:让服务在“洗澡搓背”中无缝升级
开发语言·golang·gin
期待のcode2 小时前
Jackson
java·spring boot·json
摇滚侠2 小时前
Java 零基础全套视频教程,String StringBuffer StringBuilder 类,笔记142-144、146
java·开发语言·笔记
YJlio2 小时前
杨利杰YJlio|博客导航目录(专栏总览 + 推荐阅读路线)
开发语言·python·pdf
csbysj20202 小时前
API 类别 - 特效
开发语言
wangchen_02 小时前
C++<fstream> 深度解析:文件 I/O 全指南
开发语言·前端·c++
Coder_Boy_2 小时前
基于SpringAI的智能平台基座开发-(五)
java·人工智能·spring boot·langchain·springai
步步为营DotNet2 小时前
深度探索.NET 中 IAsyncEnumerable:异步迭代的底层奥秘与高效实践
java·jvm·.net
运维行者_2 小时前
网络流量分析入门:从流量监控与 netflow 看懂核心作用
运维·开发语言·网络·云原生·容器·kubernetes·php