数组与多维数组
学习目标
- 创建、遍历、统计和复制一维数组与二维数组。
- 理解数组对象、固定长度、索引边界和引用语义。
- 使用
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:快速入口
需要快速确认数组排序和打印的基本入口时使用这段最小示例。
// 语义:需要快速确认数组排序和打印的基本入口时使用这段最小示例。
// 初始状态: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。
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 的打印、复制、排序和查找操作;排序后再二分查找,并留意方法会原地修改数组。
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)。 |
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数组字面量:创建并初始化数组
在声明数组时已知全部元素,就用字面量同时完成创建和初始化。
// 语义:在声明数组时已知全部元素,就用字面量同时完成创建和初始化。
// 初始状态: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);
// 输出:4new int[n]:按长度创建数组
只知道元素个数时用 new int[n] 创建数组,元素会取得其类型的默认值。
// 语义:只知道元素个数时用 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]);
// 输出:9array[i]:访问数组元素
使用下标读取或修改一个元素,索引必须满足 0 <= i < array.length。
// 语义:使用下标读取或修改一个元素,索引必须满足 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]);
// 输出:100array.length:读取数组长度
使用 length 属性读取数组创建后不再改变的元素个数。
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);
// 输出:Catfor:按索引遍历数组
需要索引、相邻元素或原地修改时,用普通 for 遍历数组。
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 简化遍历;循环变量不是数组槽位。
// 语义:不需要索引且只读取元素时,用增强 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]);
// 输出:1Arrays.toString:打印一维数组
使用 Arrays.toString 把一维数组格式化为可读文本,避免直接打印对象身份信息。
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 递归格式化多维或嵌套数组的元素。
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);
// 输出:2Arrays.copyOf:复制数组
需要创建新数组并复制元素时使用 Arrays.copyOf,长度变长时会用默认值补齐且引用元素仍然只是浅复制。
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 超过原长度会补默认值且非法起点会触发边界异常。
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,嵌套数组仍只复制内层引用。
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。
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。
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);
// 输出:2Arrays.fill:填充数组
需要把一个值原地写入整个数组或指定区间时使用 Arrays.fill,引用类型数组的各槽位会指向同一个对象。
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 按顺序比较两个一维数组的元素,数组引用的 == 只比较身份。
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);
// 输出:falseArrays.deepEquals:比较嵌套数组内容
使用 Arrays.deepEquals 递归比较多维或嵌套数组,而不是比较内层数组身份。
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);。
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);
// 输出:2Arrays.compare:按字典序比较(低频)
按元素顺序返回负数、零或正数,适合排序或比较版本片段。示例:int order = Arrays.compare(a, b);。
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);
// 输出:trueArrays.parallelSort:并行排序(低频)
可能利用多核处理大数组,仍然原地修改;小数组通常优先普通 sort,不要凭 API 名称假设更快。示例:Arrays.parallelSort(largeValues);。
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。
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,不能结构性修改;需要可变列表时再复制。 |
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);。
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));。
// 作用:通过 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。
// 作用:通过 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());
// 输出:3List.of:构造不可变列表
元素数量固定且不含 null 时,用 List.of 构造不可结构性修改的列表。
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();。
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继续阅读
- String 与文本处理:字符串、码元、字符集与
StringBuilder。 - 正则表达式与文本匹配:把模式编译成
Pattern并用Matcher查找。 - JSON 与 Java 对象转换:使用 Hutool
JSONUtil处理对象、数组和泛型。 - 大数与精确计算:使用
BigInteger、BigDecimal和检查溢出的MathAPI。
简单案例
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通常更合适。
课后小问
为什么
Arrays.copyOf(groups, groups.length)后修改一行会影响原数组? 答案:它只复制了外层数组的元素引用,两个二维数组仍指向同一批内层数组。 解析:这叫浅复制;若要互不影响,需要为每一行分别调用Arrays.copyOf或建立明确的深复制策略。不等长二维数组访问
rows[1][2]前要检查什么? 答案:先检查rows和行索引有效,再检查rows[1].length > 2,因为每行长度独立。 解析:二维数组是数组的数组,第二维不是统一矩阵宽度;忽略行长度会得到ArrayIndexOutOfBoundsException。
本节小结
- 数组是长度固定的对象,索引从零开始,基本元素有默认值。
- 二维数组是数组的数组,行可以不等长,访问要分层校验边界。
- 一维复制可复制内容,嵌套引用复制后仍可能共享内层对象。
Arrays适合排序、比较和复制;动态数量与查重需求应考虑集合。
快速回顾
- 能说明数组对象、数组变量和索引之间的关系。
- 能判断一维复制和二维浅复制的共享边界。
- 能用
Arrays.sort、copyOf、binarySearch、fill与deepEquals解决常见任务。 - 能区分
Arrays.asList的固定大小视图、可变副本和 primitive 数组装箱方式。 - 能根据固定长度、访问方式和增删需求选择数组或集合。
阿源的知识库