Skip to content

数组与多维数组 ​

学习目标 ​

  • 创建、遍历、统计和复制一维数组与二维数组。
  • 理解数组对象、固定长度、索引边界和引用语义。
  • 使用 Arrays 工具,并能在数组与集合之间做有依据的选择。

核心知识点 ​

专业术语 ​

  • 数组(array):长度固定、元素类型相同的对象;长度通过 array.length 读取而不是方法调用。
  • 索引(index):从 0 到 length - 1 的位置编号,越界会抛出 ArrayIndexOutOfBoundsException。
  • 二维数组:数组的元素仍然是数组,因此本质是“数组的数组”,每一行可以有不同长度。
  • 浅复制与内容复制:复制外层引用得到浅复制;Arrays.copyOf、System.arraycopy 能复制元素内容,但嵌套引用仍需逐层复制。
  • Arrays 工具类:提供 sort、copyOf、equals、deepEquals 等常用数组操作。

白话解释与边界 ​

数组对象创建后长度不能改变;需要扩容时,通常创建更大数组并复制,或者直接使用 ArrayList。基本类型数组复制的是值,引用类型数组复制的是引用,所以复制后的两个数组可能仍共享同一个可变元素。二维数组不是一块必须等宽的矩形内存,访问 rows[i][j] 时既要验证行索引,也要验证该行自己的长度。

数组适合长度已知、按索引访问频繁且内存布局简单的场景;集合更适合增删、查重或表达动态数量。Arrays.asList 的可变参数在传入 new int[]{1, 2} 时会把整个原始类型数组当成一个元素,得到大小为 1 的 List<int[]>;它不会把每个 int 自动装箱成 List<Integer>。需要整数列表时应显式遍历装箱或使用 Arrays.stream(intArray).boxed()。

常用用法 ​

数组 API:快速入口 ​

需要快速确认数组排序和打印的基本入口时使用这段最小示例。

java
// 语义:需要快速确认数组排序和打印的基本入口时使用这段最小示例。
// 初始状态:values 初始为 {3, 1, 2}。
int[] values = {3, 1, 2};
// 关键变化:values 接收表达式 {3, 1, 2} 的计算结果。
// 初始状态:values 的初始值为 {3, 1, 2}。
java.util.Arrays.sort(values);
// 关键变化:Arrays.sort(values) 按升序原地排列 {3, 1, 2},values 变为 {1, 2, 3}。
System.out.println(java.util.Arrays.toString(values));
// 输出:[1, 2, 3]

常用操作按“创建遍历 → 复制比较 → 排序查找 → 数组与集合互转”分组,下面的 常用用法 和 不常用但需要知道 标题可以直接定位。

数组语法选择说明

该小节用于快速选择数组的创建、访问和遍历写法;根据是否需要索引或修改元素选择普通 for 或增强 for。

java
int[] scores = {90, 80, 100};
// 关键变化:scores 接收表达式 {90, 80, 100} 的计算结果。
// 初始状态:scores 的初始值为 {90, 80, 100}。
int total = 0;
for (int score : scores) {
    total += score;
}
System.out.println(scores.length + ", " + total);
// 输出:3, 270
目标高频写法说明
创建并填充int[] a = {1, 2, 3};字面量只能在声明处直接使用;其他位置写 new int[]{1, 2, 3}。
创建指定长度int[] a = new int[3];基本类型元素默认是 0,引用类型元素默认是 null。
访问与长度a[i]、a.length索引从 0 开始;length 是属性,不是方法。
按索引遍历for (int i = 0; i < a.length; i++)需要索引或修改元素时使用。
遍历元素for (int value : a)只读每个元素时更简洁,修改 value 不会改数组。

Arrays:高频操作总览 ​

该小节用于快速选择 Arrays 的打印、复制、排序和查找操作;排序后再二分查找,并留意方法会原地修改数组。

java
import java.util.Arrays;

int[] values = {7, 2, 9, 2};
// 关键变化:values 接收表达式 {7, 2, 9, 2} 的计算结果。
// 初始状态:values 当前为 {7, 2, 9, 2}。
Arrays.sort(values);
// 关键变化:Arrays.sort(values) 原地升序排序,values 变为 [2, 2, 7, 9]。
// 作用:该小节用于快速选择 Arrays 的打印、复制、排序和查找操作。
int[] copy = Arrays.copyOf(values, values.length + 1);
// 初始状态:copy 当前为 Arrays.copyOf(values, values.length + 1)。
System.out.println(Arrays.toString(copy));
// 输出:[2, 2, 7, 9, 0]
任务写法关键边界
打印Arrays.toString(a)多维数组用 Arrays.deepToString(rows)。
复制Arrays.copyOf(a, newLength)返回新数组;长度变长时用默认值补齐。
复制范围Arrays.copyOfRange(a, from, to)from 包含、to 不包含;to 超过原长度会用默认值补齐。
指定位置复制System.arraycopy(src, srcPos, dst, dstPos, length)适合已知源/目标位置的批量复制;嵌套数组仍是浅复制。
排序Arrays.sort(a)排序后数组会被原地修改;范围重载同样使用左闭右开区间。
二分查找Arrays.binarySearch(a, value)必须先按同一规则排序;找到返回索引,未找到返回负的插入点编码。
填充Arrays.fill(a, value)可用 Arrays.fill(a, from, to, value) 只填充一段。
比较Arrays.equals(a, b)一维按元素比较;嵌套数组使用 Arrays.deepEquals(a, b)。
java
int[] values = {7, 2, 9, 2};
// 关键变化:values 接收表达式 {7, 2, 9, 2} 的计算结果。
// 初始状态:values 的初始值为 {7, 2, 9, 2}。
int[] copy = Arrays.copyOf(values, values.length);
// 初始状态:copy 保存 values 的浅复制,内容为 {2, 2, 7, 9}。
Arrays.sort(copy);
// 关键变化:Arrays.sort(copy) 保持升序,copy 仍为 {2, 2, 7, 9}。
int index = Arrays.binarySearch(copy, 7); // 先排序,再查找
// 返回:Arrays.binarySearch(copy, 7) 找到 7 的索引 2,index 为 2。
Arrays.fill(values, 0, 1, 99);           // 只改 values[0]
// 关键变化:Arrays.fill(values, 0, 1, 99) 把 values[0] 改为 99,values 变为 {99, 2, 9, 2}。
System.out.println(Arrays.toString(copy) + ", index=" + index);
// 输出:[2, 2, 7, 9], index=2

数组字面量:创建并初始化数组 ​

在声明数组时已知全部元素,就用字面量同时完成创建和初始化。

java
// 语义:在声明数组时已知全部元素,就用字面量同时完成创建和初始化。
// 初始状态:numbers 初始为 {1, 2, 3};first 初始为 numbers[0]。
int[] numbers = {1, 2, 3};
// 关键变化:numbers 接收表达式 {1, 2, 3} 的计算结果。
// 初始状态:numbers 的初始值为 {1, 2, 3}。
int first = numbers[0];
int last = numbers[numbers.length - 1];
int total = first + last;
System.out.println(numbers.length);
// 输出:3
System.out.println(total);
// 输出:4

new int[n]:按长度创建数组 ​

只知道元素个数时用 new int[n] 创建数组,元素会取得其类型的默认值。

java
// 语义:只知道元素个数时用 new int[n] 创建数组,元素会取得其类型的默认值。
// 初始状态:size 初始为 3;numbers 初始为 new int[size]。
int size = 3;
// 关键变化:size 接收表达式 3 的计算结果。
// 初始状态:size 当前为 3。
int[] numbers = new int[size];
// 返回:new int[size] 创建长度为 3 的 int 数组,元素默认都是 0。
// 作用:只知道元素个数时用 new int[n] 创建数组,元素会取得其类型的默认值。
int first = numbers[0];
numbers[1] = 9;
System.out.println(first);
// 输出:0
System.out.println(numbers[1]);
// 输出:9

array[i]:访问数组元素 ​

使用下标读取或修改一个元素,索引必须满足 0 <= i < array.length。

java
// 语义:使用下标读取或修改一个元素,索引必须满足 0 <= i < array.length。
// 初始状态:numbers 初始为 {10, 20, 30};value 初始为 numbers[1]。
int[] numbers = {10, 20, 30};
// 关键变化:numbers 接收表达式 {10, 20, 30} 的计算结果。
// 初始状态:numbers 当前为 {10, 20, 30}。
numbers[1] = 99;
int value = numbers[1];
numbers[2] = value + 1;
System.out.println(value);
// 输出:99
// 作用:使用下标读取或修改一个元素,索引必须满足 0 <= i < array.length。
System.out.println(numbers[2]);
// 输出:100

array.length:读取数组长度 ​

使用 length 属性读取数组创建后不再改变的元素个数。

java
String[] names = {"Ann", "Bob", "Cat"};
// 关键变化:names 接收表达式 {"Ann", "Bob", "Cat"} 的计算结果。
// 初始状态:names 当前为 {"Ann", "Bob", "Cat"}。
int size = names.length;
// 初始状态:size 当前为 names.length。
// 作用:使用 length 属性读取数组创建后不再改变的元素个数。
int lastIndex = size - 1;
String last = names[lastIndex];
System.out.println(size);
// 输出:3
System.out.println(last);
// 输出:Cat

for:按索引遍历数组 ​

需要索引、相邻元素或原地修改时,用普通 for 遍历数组。

java
int[] numbers = {1, 2, 3};
// 初始状态:numbers 当前为 {1, 2, 3}。
int total = 0;
// 初始状态:total 当前为 0。
for (int i = 0; i < numbers.length; i++) {
// 作用:需要索引、相邻元素或原地修改时,用普通 for 遍历数组。
    numbers[i] *= 2;
    total += numbers[i];
}
System.out.println(total);
// 输出:12

增强 for:逐个读取数组元素 ​

不需要索引且只读取元素时,用增强 for 简化遍历;循环变量不是数组槽位。

java
// 语义:不需要索引且只读取元素时,用增强 for 简化遍历。
// 初始状态:numbers 初始为 {1, 2, 3};total 初始为 0。
int[] numbers = {1, 2, 3};
// 初始状态:numbers 的初始值为 {1, 2, 3}。
int total = 0;
for (int number : numbers) {
// 作用:增强 for 按条件逐项处理本行输入。
    total += number;
}
System.out.println(total);
// 输出:6
System.out.println(numbers[0]);
// 输出:1

Arrays.toString:打印一维数组 ​

使用 Arrays.toString 把一维数组格式化为可读文本,避免直接打印对象身份信息。

java
import java.util.Arrays;

int[] numbers = {1, 2};
// 初始状态:numbers 当前为 {1, 2}。
String text = Arrays.toString(numbers);
// 初始状态:text 当前为 Arrays.toString(numbers)。
// 作用:使用 Arrays.toString 把一维数组格式化为可读文本,避免直接打印对象身份信息。
// 关键变化:Arrays.toString(numbers) 将 [1, 2] 格式化为字符串 "[1, 2]" 并写入 text。
numbers[0] = 9;
String changed = Arrays.toString(numbers);
// 返回:changed 接收 Arrays.toString(numbers) 的返回值。
// 关键变化:Arrays.toString(numbers) 读取 numbers=[9, 2] 并把格式化文本写入 changed,changed 变为 "[9, 2]"。
System.out.println(text);
// 输出:[1, 2]
System.out.println(changed);
// 输出:[9, 2]

Arrays.deepToString:打印嵌套数组 ​

使用 Arrays.deepToString 递归格式化多维或嵌套数组的元素。

java
import java.util.Arrays;

int[][] rows = {{1, 2}, {3, 4}};
// 初始状态:rows 当前为 {{1, 2}, {3, 4}}。
String text = Arrays.deepToString(rows);
// 初始状态:text 当前为 Arrays.deepToString(rows)。
// 作用:使用 Arrays.deepToString 递归格式化多维或嵌套数组的元素。
int rowCount = rows.length;
System.out.println(text);
// 输出:[[1, 2], [3, 4]]
System.out.println(rowCount);
// 输出:2

Arrays.copyOf:复制数组 ​

需要创建新数组并复制元素时使用 Arrays.copyOf,长度变长时会用默认值补齐且引用元素仍然只是浅复制。

java
import java.util.Arrays;

int[] source = {1, 2};
// 初始状态:source 当前为 {1, 2}。
int[] copy = Arrays.copyOf(source, 4);
// 返回:Arrays.copyOf(source, 4) 复制 [1, 2] 并用默认值补齐,copy 为 [1, 2, 0, 0]。
// 作用:需要创建新数组并复制元素时使用 Arrays.copyOf,长度变长时会用默认值补齐且引用元素仍然只是浅复制。
copy[0] = 99;
System.out.println(Arrays.toString(source) + " / " + Arrays.toString(copy));
// 输出:[1, 2] / [99, 2, 0, 0]

Arrays.copyOfRange:复制数组区间 ​

需要复制左闭右开区间时使用 Arrays.copyOfRange,to 超过原长度会补默认值且非法起点会触发边界异常。

java
import java.util.Arrays;

int[] source = {10, 20, 30, 40};
// 初始状态:source 当前为 {10, 20, 30, 40}。
int[] middle = Arrays.copyOfRange(source, 1, 3);
// 返回:Arrays.copyOfRange(source, 1, 3) 取左闭右开区间,middle 为 [20, 30]。
// 作用:需要复制左闭右开区间时使用 Arrays.copyOfRange,to 超过原长度会补默认值且非法起点会触发边界异常。
System.out.println(Arrays.toString(middle));
// 输出:[20, 30]

System.arraycopy:按位置复制 ​

需要在已有源数组和目标数组的明确位置之间批量搬运元素时使用 System.arraycopy,嵌套数组仍只复制内层引用。

java
int[] source = {1, 2, 3};
// 初始状态:source 当前为 {1, 2, 3}。
int[] target = {0, 0, 0, 0, 0};
// 初始状态:target 当前为 {0, 0, 0, 0, 0}。
System.arraycopy(source, 0, target, 1, source.length);
// 关键变化:System.arraycopy(source, 0, target, 1, source.length) 把 [1, 2, 3] 写入 target 的索引 1..3。
// 作用:需要在已有源数组和目标数组的明确位置之间批量搬运元素时使用 System.arraycopy,嵌套数组仍只复制内层引用。
System.out.println(java.util.Arrays.toString(target));
// 输出:[0, 1, 2, 3, 0]

Arrays.sort:原地排序 ​

需要原地排序数组时使用 Arrays.sort,区间重载采用左闭右开范围且只有引用类型数组可以传入 Comparator。

java
import java.util.Arrays;

int[] numbers = {3, 1, 2};
// 初始状态:numbers 当前为 {3, 1, 2}。
Arrays.sort(numbers);
// 关键变化:Arrays.sort(numbers) 原地升序排列 numbers,变为 [1, 2, 3]。
// 作用:需要原地排序数组时使用 Arrays.sort,区间重载采用左闭右开范围且只有引用类型数组可以传入 Comparator。
System.out.println(Arrays.toString(numbers));
// 输出:[1, 2, 3]

Arrays.binarySearch:二分查找 ​

需要在已按相同规则排序的数组中二分查找时使用 Arrays.binarySearch,未找到时返回 -(插入点) - 1。

java
import java.util.Arrays;

int[] sorted = {1, 3, 5, 7};
// 初始状态:sorted 当前为 {1, 3, 5, 7}。
int index = Arrays.binarySearch(sorted, 5);
// 初始状态:index 当前为 Arrays.binarySearch(sorted, 5)。
// 作用:需要在已按相同规则排序的数组中二分查找时使用 Arrays.binarySearch,未找到时返回 -(插入点) - 1。
System.out.println(index);
// 输出:2

Arrays.fill:填充数组 ​

需要把一个值原地写入整个数组或指定区间时使用 Arrays.fill,引用类型数组的各槽位会指向同一个对象。

java
import java.util.Arrays;

int[] numbers = new int[4];
// 初始状态:numbers 当前为 new int[4]。
Arrays.fill(numbers, -1);
// 作用:需要把一个值原地写入整个数组或指定区间时使用 Arrays.fill,引用类型数组的各槽位会指向同一个对象。
// 关键变化:Arrays.fill(numbers, -1) 将 4 个槽位全部写成 -1,numbers 变为 {-1, -1, -1, -1}。
Arrays.fill(numbers, 1, 3, 9);
// 关键变化:Arrays.fill(numbers, 1, 3, 9) 把索引 1(含)到 3(不含)的位置写成 9,numbers 变为 [-1, 9, 9, -1]。
System.out.println(Arrays.toString(numbers));
// 输出:[-1, 9, 9, -1]

Arrays.equals:比较一维数组内容 ​

使用 Arrays.equals 按顺序比较两个一维数组的元素,数组引用的 == 只比较身份。

java
import java.util.Arrays;

int[] left = {1, 2};
// 初始状态:left 当前为 {1, 2}。
int[] right = {1, 2};
// 初始状态:right 当前为 {1, 2}。
boolean sameContent = Arrays.equals(left, right);
// 作用:使用 Arrays.equals 按顺序比较两个一维数组的元素,数组引用的 == 只比较身份。
boolean sameObject = left == right;
System.out.println(sameContent);
// 输出:true
System.out.println(sameObject);
// 输出:false

Arrays.deepEquals:比较嵌套数组内容 ​

使用 Arrays.deepEquals 递归比较多维或嵌套数组,而不是比较内层数组身份。

java
import java.util.Arrays;

int[][] left = {{1}, {2}};
// 初始状态:left 当前为 {{1}, {2}}。
int[][] right = {{1}, {2}};
// 初始状态:right 当前为 {{1}, {2}}。
boolean same = Arrays.deepEquals(left, right);
// 作用:使用 Arrays.deepEquals 递归比较多维或嵌套数组,而不是比较内层数组身份。
System.out.println(same);
// 输出:true
System.out.println(left[0] == right[0]);
// 输出:false

不常用但需要知道 ​

Arrays.mismatch:查找首个差异(低频) ​

返回两个数组第一个不同的索引,完全相同返回 -1;适合定位测试或协议差异。示例:int diff = Arrays.mismatch(left, right);。

java
import java.util.Arrays;

int[] left = {1, 2, 8};
// 初始状态:left 当前为 {1, 2, 8}。
int[] right = {1, 2, 9};
// 初始状态:right 为 {1, 2, 9},与 left 在索引 2 首次不同。
int diff = Arrays.mismatch(left, right);
// 返回:Arrays.mismatch(left, right) 比较 [1, 2, 8] 与 [1, 2, 9],返回首个差异索引 2。
// 关键变化:Arrays.mismatch(left, right) 查找到索引 2 的首个差异,diff 变为 2。
System.out.println(diff);
// 输出:2

Arrays.compare:按字典序比较(低频) ​

按元素顺序返回负数、零或正数,适合排序或比较版本片段。示例:int order = Arrays.compare(a, b);。

java
import java.util.Arrays;

int order = Arrays.compare(new int[]{1, 2}, new int[]{1, 3});
// 输入:order 的初始值为 Arrays.compare(new int[]{1, 2}, new int[]{1, 3})。
// 返回:Arrays.compare(new int[]{1, 2}, new int[]{1, 3}) 返回负数,因此 order < 0 为 true。
// 关键变化:Arrays.compare(new int[]{1, 2}, new int[]{1, 3}) 按字典序比较后让 order 成为负数。
System.out.println(order < 0);
// 输出:true

Arrays.parallelSort:并行排序(低频) ​

可能利用多核处理大数组,仍然原地修改;小数组通常优先普通 sort,不要凭 API 名称假设更快。示例:Arrays.parallelSort(largeValues);。

java
import java.util.Arrays;

int[] values = {5, 2, 4, 1, 3};
// 初始状态:values 当前为 {5, 2, 4, 1, 3}。
Arrays.parallelSort(values);
// 关键变化:Arrays.parallelSort(values) 原地排序 [5, 2, 4, 1, 3],values 变为 [1, 2, 3, 4, 5]。
System.out.println(Arrays.toString(values));
// 输出:[1, 2, 3, 4, 5]

数组与集合互转 ​

数组转 List 选择说明

该小节用于选择引用数组或 primitive 数组转 List 的写法;Arrays.asList 得到固定大小视图,primitive 数组需要先 boxed。

java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

String[] names = {"Ann", "Bo"};
List<String> view = Arrays.asList(names);
// 返回:Arrays.asList(names) 返回与 names 共享存储的固定大小列表 view。
// 关键变化:Arrays.asList(names) 创建固定大小视图,view 内容为 [Ann, Bo] 并与 names 共享元素。
int[] numbers = {1, 2};
List<Integer> boxed = Arrays.stream(numbers).boxed().collect(Collectors.toCollection(ArrayList::new));
// 返回:Arrays.stream(numbers).boxed().collect(...) 把 {1, 2} 转成可变 List [1, 2]。
System.out.println(view + " / " + boxed);
// 输出:[Ann, Bo] / [1, 2]
场景写法得到的结果
引用类型数组转视图List<String> view = Arrays.asList(names);固定大小视图;允许 set,不允许 add/remove,数组与列表会互相看到元素替换。
得到可增删列表List<String> list = new ArrayList<>(Arrays.asList(names));复制元素引用后的可变 ArrayList;增删不会改变原数组长度。
int[] 转整数列表Arrays.stream(nums).boxed().collect(Collectors.toCollection(ArrayList::new))显式装箱为可变 List<Integer>;不要把 Arrays.asList(nums) 当成整数列表。
小型不可变列表List.of("a", "b")不接受 null,不能结构性修改;需要可变列表时再复制。
java
int[] nums = {1, 2, 3};
List<int[]> wrong = Arrays.asList(nums); // size == 1,唯一元素是整个 int[]
// 返回:Arrays.asList(nums) 只产生一个元素的 List<int[]>,该元素就是整个 nums 数组。
List<Integer> boxed = Arrays.stream(nums).boxed().collect(Collectors.toCollection(ArrayList::new));
// 返回:Arrays.stream(nums).boxed().collect(...) 把 [1, 2, 3] 转成可变列表 [1, 2, 3]。

Arrays.asList:引用数组转固定大小 List ​

返回数组的固定大小视图,允许 set 但不允许 add/remove,数组与列表会互相看到元素替换。示例:List<String> view = Arrays.asList(names);。

java
import java.util.Arrays;
import java.util.List;

String[] names = {"Ann", "Bo"};
// 初始状态:names 当前为 {"Ann", "Bo"}。
List<String> view = Arrays.asList(names);
// 返回:Arrays.asList(names) 返回与 names 共享存储的固定大小视图 [Ann, Bo]。
// 关键变化:Arrays.asList(names) 创建固定大小视图,view 内容为 [Ann, Bo] 并与 names 共享元素。
view.set(0, "Ana");
// 关键变化:view.set(0, "Ana") 修改共享数组 names[0],names 与 view 都变为 [Ana, Bo]。
System.out.println(names[0] + " / " + view);
// 输出:Ana / [Ana, Bo]

Arrays.stream(...).boxed:primitive 数组转 List ​

Arrays.asList(intArray) 会得到一个只有整个 int[] 的列表;先建立流再 boxed 才能得到 List<Integer>。示例:List<Integer> boxed = Arrays.stream(nums).boxed().collect(Collectors.toCollection(ArrayList::new));。

java
// 作用:通过 Arrays.stream(...).boxed primitive 数组转 List。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

int[] numbers = {1, 2, 3};
// 初始状态:numbers 为 {1, 2, 3}。
List<Integer> boxed = Arrays.stream(numbers).boxed().collect(Collectors.toList());
// 返回:Arrays.stream(numbers).boxed().collect(Collectors.toList()) 返回不可保证可变性的 List [1, 2, 3]。
System.out.println(boxed);
// 输出:[1, 2, 3]

new ArrayList<>(...):构造可变列表 ​

需要在转换后增删元素时,把现有集合复制进新的可变 ArrayList。

java
// 作用:通过 new ArrayList<>(...) 构造可变列表。
import java.util.ArrayList;
import java.util.List;

List<String> mutable = new ArrayList<>(List.of("a", "b"));
// 初始状态:mutable 是由 List.of("a", "b") 初始化的可变 ArrayList,内容为 [a, b]。
mutable.add("c");
// 关键变化:mutable.add("c") 在末尾追加 c,mutable 变为 [a, b, c]。
System.out.println(mutable);
// 输出:[a, b, c]
System.out.println(mutable.size());
// 输出:3

List.of:构造不可变列表 ​

元素数量固定且不含 null 时,用 List.of 构造不可结构性修改的列表。

java
import java.util.List;

List<String> immutable = List.of("a", "b");
// 输入:immutable 的初始值为 List.of("a", "b")。
// 返回:List.of("a", "b") 创建不可变列表 [a, b],不能执行 add/remove。
// 关键变化:List.of("a", "b") 创建不可变列表,immutable 固定为 [a, b]。
String first = immutable.get(0);
System.out.println(immutable);
// 输出:[a, b]
System.out.println(first);
// 输出:a

不常用但需要知道:流式转换 ​

IntStream.of:primitive 数组流式转换(低频) ​

将 int[] 直接转成 IntStream,适合统计或映射;需要 List<Integer> 仍要 boxed。示例:long count = IntStream.of(nums).filter(n -> n > 0).count();。

java
import java.util.stream.IntStream;

int[] numbers = {1, -2, 3};
// 初始状态:numbers 当前为 {1, -2, 3}。
long positive = IntStream.of(numbers).filter(number -> number > 0).count();
// 返回:IntStream.of(numbers).filter(number -> number > 0).count() 统计 [1, -2, 3] 中的正数,positive 为 2。
// 关键变化:IntStream.of(numbers) 过滤正数后 count() 得到 2,positive 变为 2。
System.out.println(positive);
// 输出:2

继续阅读 ​

简单案例 ​

java
import java.util.Arrays;

public class ArrayDemo {
    public static void main(String[] args) {
        int[] scores = {88, 76, 95, 82};
// 初始状态:scores 为 [88, 76, 95, 82]。
        int[] original = Arrays.copyOf(scores, scores.length);
// 返回:Arrays.copyOf(scores, scores.length) 复制出 original=[88, 76, 95, 82],与 scores 独立。
        Arrays.sort(scores);
// 关键变化:Arrays.sort(scores) 原地升序排序,scores 变为 [76, 82, 88, 95]。
        int[][] groups = {{88, 76}, {95, 82, 91}}; // 行长度可以不同
// 初始状态:groups 有 2 行,第二行长度为 3。

        int total = 0;
        for (int score : original) {
            total += score;
        }
        int[][] shallow = Arrays.copyOf(groups, groups.length);
// 返回:Arrays.copyOf(groups, groups.length) 只复制外层数组,shallow[0] 仍与 groups[0] 共享。
        shallow[0][0] = 0; // 内层数组仍与 groups[0] 共享

        System.out.println("sorted=" + Arrays.toString(scores));
        // 输出:sorted=[76, 82, 88, 95]
        System.out.println("average=" + (total / (double) original.length));
        // 输出:average=85.25
        System.out.println("rows=" + groups.length + ", secondRow=" + groups[1].length);
        // 输出:rows=2, secondRow=3
        System.out.println("shared=" + groups[0][0]);
        // 输出:shared=0
    }
}

这个案例把排序、平均值、二维数组行长度和浅复制共享边界放在一起观察;排序只改动 scores,而 shallow 与原二维数组共享内层行。

易混点 ​

  • array.length 是固定属性,不能写成 array.length();数组越界属于运行时错误,不会静默返回空值。
  • Arrays.copyOf 对一维基本类型数组是内容复制,对二维数组只复制第一层引用,不能自动完成深复制。
  • Arrays.equals 比较一维元素,Arrays.deepEquals 才按嵌套数组内容比较;直接比较数组变量只比较身份。
  • Arrays.binarySearch 只能用于已按同一规则排序的数组;未找到时的负数不是普通的 -1,而是编码后的插入位置。
  • Arrays.asList 是固定大小视图,new ArrayList<>(...) 才能正常增删;原始类型数组还会触发“整个数组作为一个元素”的陷阱。
  • 数组长度固定,集合容量可按需求增长;需要频繁插入、删除或去重时,ArrayList/Set 通常更合适。

课后小问 ​

  1. 为什么 Arrays.copyOf(groups, groups.length) 后修改一行会影响原数组? 答案:它只复制了外层数组的元素引用,两个二维数组仍指向同一批内层数组。 解析:这叫浅复制;若要互不影响,需要为每一行分别调用 Arrays.copyOf 或建立明确的深复制策略。

  2. 不等长二维数组访问 rows[1][2] 前要检查什么? 答案:先检查 rows 和行索引有效,再检查 rows[1].length > 2,因为每行长度独立。 解析:二维数组是数组的数组,第二维不是统一矩阵宽度;忽略行长度会得到 ArrayIndexOutOfBoundsException。

本节小结 ​

  • 数组是长度固定的对象,索引从零开始,基本元素有默认值。
  • 二维数组是数组的数组,行可以不等长,访问要分层校验边界。
  • 一维复制可复制内容,嵌套引用复制后仍可能共享内层对象。
  • Arrays 适合排序、比较和复制;动态数量与查重需求应考虑集合。

快速回顾 ​

  • 能说明数组对象、数组变量和索引之间的关系。
  • 能判断一维复制和二维浅复制的共享边界。
  • 能用 Arrays.sort、copyOf、binarySearch、fill 与 deepEquals 解决常见任务。
  • 能区分 Arrays.asList 的固定大小视图、可变副本和 primitive 数组装箱方式。
  • 能根据固定长度、访问方式和增删需求选择数组或集合。