Arrays 工具类有哪些常用方法?数组和 List 怎么互相转换?
简化版
Arrays 是操作数组的工具类(类比 Collections 操作集合),全是静态方法。常用方法:① 排序查找(sort 排序、binarySearch 二分查找、parallelSort 并行排序);② 转换(asList 数组转 List、stream 数组转流);③ 复制(copyOf 复制指定长度、copyOfRange 复制一段);④ 填充比较(fill 填充、equals 比较、deepEquals 深度比较多维数组、toString/deepToString 打印)。数组和 List 互转是高频:数组转 List 用 Arrays.asList(arr)(注意坑:返回定长视图、基本类型数组会变单元素)或 Arrays.stream(arr).boxed().collect(toList());List 转数组 用 list.toArray(new String[0])。
详细版
Arrays 常用方法:
int[] arr = {3, 1, 2};
// ① 排序、查找
Arrays.sort(arr); // 排序 → [1, 2, 3](基本类型用双轴快排)
Arrays.sort(arr, 0, 2); // 排序指定范围
int idx = Arrays.binarySearch(arr, 2); // 二分查找(需已排序)
Arrays.parallelSort(bigArr); // 并行排序(大数组、多核加速)
// ② 复制
int[] copy = Arrays.copyOf(arr, 5); // 复制成长度 5(不足补 0)→ [1,2,3,0,0]
int[] range = Arrays.copyOfRange(arr, 1, 3); // 复制索引 [1,3) → [2, 3]
// ③ 填充
int[] filled = new int[3];
Arrays.fill(filled, 7); // 全部填 7 → [7, 7, 7]
// ④ 比较、打印
Arrays.equals(arr, copy); // 比较两个数组内容是否相等(一维)
Arrays.deepEquals(matrix1, matrix2); // 深度比较(多维数组)
Arrays.toString(arr); // "[1, 2, 3]"(一维,别直接 arr.toString())
Arrays.deepToString(matrix); // 多维数组打印
// ⑤ 转换
List<Integer> list = Arrays.asList(1, 2, 3); // 转 List(有坑,见下)
IntStream stream = Arrays.stream(arr); // 转流
数组 ⇄ List 互转:
// 数组 → List
String[] strArr = {"a", "b"};
List<String> list1 = Arrays.asList(strArr); // 定长视图(不能 add/remove,见坑)
List<String> list2 = new ArrayList<>(Arrays.asList(strArr)); // 可变的独立 List
List<String> list3 = Arrays.stream(strArr).collect(toList()); // Stream 方式
// List → 数组
List<String> list = List.of("a", "b");
String[] arr1 = list.toArray(new String[0]); // 推荐写法
Object[] arr2 = list.toArray(); // 返回 Object[](丢了泛型)
⚠️
Arrays.asList有几个经典坑(前面「Arrays.asList 的坑」题详讲):① 返回定长视图(add/remove抛 UnsupportedOperationException);② 是数组的视图(改 List 会改原数组);③ 传基本类型数组会变单元素(Arrays.asList(new int[]{1,2,3})得到List<int[]>size=1,要用Integer[])。要可变独立 List 用new ArrayList<>(Arrays.asList(arr))。
完整版教学
一、Arrays 是什么:操作数组的工具类
Arrays 是 java.util.Arrays——操作数组的静态工具类,和 Collections(操作集合)是平行的:
数组(int[]、String[]…)本身没有很多方法(不像集合有 add/remove/sort)
数组只有 length 属性和下标访问,没有排序、查找、复制等方法
→ 需要一个工具类来提供这些操作 → Arrays
Arrays 提供的:排序、查找、复制、填充、比较、打印、转换等静态方法
用法:Arrays.sort(arr)、Arrays.copyOf(arr, n) 这样调
Arrays 弥补了「数组本身功能贫乏」的问题——数组是最底层的数据结构(连续内存、下标访问),没有集合那样丰富的方法,Arrays 工具类补上了排序、查找、复制等常用操作。它和 Collections(集合工具类)、Objects(对象工具类)是同类。理解「Arrays 是操作数组的静态工具类、补充数组的功能」,就理解了它的定位——数组的「功能扩展工具箱」。
二、排序与查找
Arrays 的排序查找是最常用的(前面「TimSort」题讲了算法,这里讲用法):
// 排序(基本类型双轴快排、对象 TimSort)
Arrays.sort(arr); // 整个数组排序
Arrays.sort(arr, 1, 4); // 排序索引 [1, 4) 这一段
Arrays.sort(users, Comparator.comparing(User::getAge)); // 对象数组自定义排序
// 并行排序(大数组,利用多核)
Arrays.parallelSort(bigArr); // 数据量大时比 sort 快(Fork/Join 并行归并)
// 二分查找(必须先排序!)
int idx = Arrays.binarySearch(arr, key); // 找到返回下标,找不到返回负数
要点:① sort 可排整个数组或指定范围(sort(arr, from, to));② 对象数组要可比较或传 Comparator;③ parallelSort 用于大数组(多核并行,小数组反而因并行开销更慢);④ binarySearch 必须先排序(对未排序数组用二分查找结果不可靠,且找不到时返回 -(插入点)-1 的负数,可用于定位插入位置)。理解「Arrays.sort 排序(可指定范围)、parallelSort 并行、binarySearch 需先排序」,就掌握了 Arrays 的排序查找。
三、复制:copyOf 与 copyOfRange
Arrays 的复制方法用于「创建数组的副本」——因为数组没有内置的复制方法:
int[] arr = {1, 2, 3};
// copyOf:复制成指定长度(不足补默认值、超出截断)
int[] a = Arrays.copyOf(arr, 5); // [1, 2, 3, 0, 0](补 0)
int[] b = Arrays.copyOf(arr, 2); // [1, 2](截断)
// copyOfRange:复制指定范围 [from, to)
int[] c = Arrays.copyOfRange(arr, 1, 3); // [2, 3](索引 1~2)
// 这些都是"深拷贝一维"(新数组、独立于原数组)
c[0] = 99; // 不影响 arr
复制方法的用途:① copyOf(arr, newLength)——复制成指定长度(常用于「数组扩容」,如 ArrayList 扩容底层就用它);② copyOfRange(arr, from, to)——复制一段(类似 List 的 subList 但是独立副本不是视图)。注意这些是「一维深拷贝」——对一维基本类型数组是真复制(独立);但对「对象数组」或「多维数组」是浅拷贝(只复制第一维的引用,元素对象/子数组还是共享的)。还有 System.arraycopy(更底层的复制,Arrays.copyOf 内部就用它)。理解「copyOf 复制指定长度、copyOfRange 复制一段、都是独立副本、一维深拷贝多维浅拷贝」,就掌握了数组复制。
四、比较与打印:别直接用 == 和 toString
数组的比较和打印有两个必须知道的坑——不能直接用 == 比较、不能直接 toString:
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
// ✗ 直接 == 比较的是引用(地址),不是内容
a == b; // false(两个不同的数组对象)
// ✓ 用 Arrays.equals 比较内容
Arrays.equals(a, b); // true(内容相同)
Arrays.deepEquals(matrix1, matrix2); // 多维数组用 deepEquals
// ✗ 直接 toString 打印的是"类型@哈希码"
a.toString(); // "[I@1b6d3586"(没用)
// ✓ 用 Arrays.toString 打印内容
Arrays.toString(a); // "[1, 2, 3]"
Arrays.deepToString(matrix); // 多维数组用 deepToString
两个坑:① 比较数组内容用 Arrays.equals(数组没重写 equals,直接 == 或 a.equals(b) 比的是引用地址);② 打印数组内容用 Arrays.toString(数组没重写 toString,直接打印是「[I@哈希码」这种无意义的)。多维数组用 deepEquals/deepToString(递归处理每一维)。这是新手常踩的坑——想比较/打印数组内容,却用了 ==/toString。理解「数组比较内容用 Arrays.equals、打印用 Arrays.toString、多维用 deep 版本」,就避开了数组比较打印的坑。
五、数组转 List:asList 与它的坑
「数组 → List」是高频操作,主要用 Arrays.asList,但它有坑(前面详讲过,这里聚焦转换):
String[] arr = {"a", "b", "c"};
// Arrays.asList:数组转 List——但是"定长视图"
List<String> list = Arrays.asList(arr);
list.add("x"); // ✗ UnsupportedOperationException(定长,不能增删)
list.set(0, "z"); // ✓ 能改(改会同步到原数组,因为是视图)
// 要"可变的独立 List" → 包一层 ArrayList
List<String> mutable = new ArrayList<>(Arrays.asList(arr));
mutable.add("x"); // ✓ 可增删、且独立于原数组
// 基本类型数组的坑:变成单元素 List<int[]>!
int[] intArr = {1, 2, 3};
List list2 = Arrays.asList(intArr); // ⚠️ List<int[]> size=1(整个数组当一个元素)
// 正确:用 Integer[] 或 Stream
List<Integer> list3 = Arrays.stream(intArr).boxed().collect(toList()); // ✓ size=3
三个要点:① Arrays.asList 返回定长视图(不能 add/remove、且改会同步到原数组);② 要可变独立 List 用 new ArrayList<>(Arrays.asList(arr));③ 基本类型数组会被当成「一个元素」(Arrays.asList(int[]) 得到 List<int[]> size=1,因为泛型不认基本类型——要用 Integer[] 或 Arrays.stream(intArr).boxed())。理解「asList 返回定长视图、要独立用 new ArrayList、基本类型数组会变单元素」,就掌握了「数组转 List」及其坑。
六、List 转数组:toArray
「List → 数组」用 toArray,也有讲究:
List<String> list = List.of("a", "b", "c");
// 方式1:toArray()——返回 Object[](丢了泛型,要强转,不推荐)
Object[] arr1 = list.toArray();
// 方式2:toArray(T[])——返回正确类型的数组(推荐)
String[] arr2 = list.toArray(new String[0]); // ★ 推荐写法(传长度 0 的数组)
// 传 new String[0] 而非 new String[list.size()]:
// 现代 JVM 下 new String[0] 反而更快(JIT 优化),且不用算 size,是官方推荐
// Java 11+ 新方式
String[] arr3 = list.toArray(String[]::new); // 用构造器引用(也推荐)
要点:① toArray()(无参)返回 Object[](丢了泛型,要强转成具体类型,不推荐);② toArray(new String[0])(传类型数组)返回正确类型的数组(推荐——传长度 0 的空数组即可,toArray 会创建合适大小的数组,且 new String[0] 在现代 JVM 下反而比 new String[size] 快);③ Java 11+ 可用 toArray(String[]::new)(构造器引用,也推荐)。别用 toArray()(Object[] 丢泛型)。理解「List 转数组用 toArray(new T[0])(推荐)、别用无参 toArray(返回 Object[])」,就掌握了「List 转数组」的正确姿势。
记忆钩子:「Arrays 是操作数组的工具类(类比 Collections):sort 排序(可指定范围、parallelSort 并行)、binarySearch 查找(需先排序)、copyOf/copyOfRange 复制(独立副本)、fill 填充、equals 比较内容(别用 ==)、toString 打印(别直接 toString)、多维用 deepEquals/deepToString;数组转 List 用 Arrays.asList(定长视图有坑,要独立用 new ArrayList,基本类型数组会变单元素用 stream.boxed)、List 转数组用 list.toArray(new T[0])(别用无参返回 Object[])」。
七、常见误区与追问
- 误区:比较两个数组内容用
==或equals。 数组没重写 equals,==和a.equals(b)比的是引用地址;比较内容要用Arrays.equals(多维用Arrays.deepEquals)。 - 误区:打印数组用
arr.toString()。 数组没重写 toString,直接打印是「[I@哈希码」;打印内容用Arrays.toString(arr)(多维用Arrays.deepToString)。 - 误区:
Arrays.asList(int[])得到含 3 个元素的 List。 得到List<int[]>size=1(整个数组当一个元素,因为泛型不认基本类型);要用Integer[]或Arrays.stream(intArr).boxed().collect(toList())。 - 误区:
Arrays.asList返回可变的独立 List。 返回定长视图(不能 add/remove、改会同步到原数组);要可变独立 List 用new ArrayList<>(Arrays.asList(arr))。 - 追问:数组怎么转 List,有什么坑? 用
Arrays.asList(arr)(返回定长视图、改会同步原数组、基本类型数组会变单元素);要可变独立 List 用new ArrayList<>(Arrays.asList(arr));基本类型数组用Arrays.stream(arr).boxed().collect(toList())。 - 追问:List 怎么转数组? 用
list.toArray(new String[0])(推荐——返回正确类型、传长度 0 的空数组即可、现代 JVM 下 new String[0] 更快)或 Java 11+ 的list.toArray(String[]::new);别用无参toArray()(返回 Object[]、丢泛型)。 - 追问:Arrays.copyOf 是深拷贝还是浅拷贝? 一维基本类型数组是真复制(独立);但对象数组和多维数组是浅拷贝——只复制第一维的引用,元素对象/子数组仍共享。
八、加强记忆
Arrays 是操作数组的静态工具类(和 Collections 操作集合平行,补充数组贫乏的功能)。常用方法:① 排序查找(sort 排序(可指定范围、parallelSort 大数组并行)、binarySearch 二分查找(必须先排序));② 复制(copyOf(arr, n) 复制成指定长度、copyOfRange(arr, from, to) 复制一段,都是独立副本、一维深拷贝多维浅拷贝);③ 填充(fill);④ 比较打印(比较内容用 Arrays.equals(别用 ==,数组没重写 equals 比的是地址)、打印用 Arrays.toString(别直接 toString,是「[I@哈希码」)、多维用 deepEquals/deepToString)。数组 ⇄ List 互转(高频):数组转 List 用 Arrays.asList(arr)(定长视图有坑——不能 add/remove、改会同步原数组、基本类型数组会变单元素 List<int[]>,要可变独立用 new ArrayList<>(Arrays.asList(arr))、基本类型用 Arrays.stream(arr).boxed().collect(toList()));List 转数组 用 list.toArray(new String[0])(推荐,别用无参 toArray() 返回 Object[] 丢泛型)。一句话「Arrays 操作数组:sort/binarySearch(需先排序)/copyOf/equals(别用==)/toString(别直接)、多维用 deep 版;数组转 List 用 asList(定长视图有坑)、List 转数组用 toArray(new T[0])」。