数组是什么
数组就是一排储物柜,每个柜子有个编号(下标),柜子数量在建好的时候就固定了,不能增减
特点:类型相同、长度固定、下标从0开始
想象一下学校走廊的储物柜:一排10个,编号从0到9,每个柜子只能放同一种东西(比如都放书或都放鞋)
声明和初始化
两种方式:你知道要放什么就直接列出来(静态初始化),只知道要几个柜子就先建空柜子(动态初始化)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Test public void testArrayInit() { System.out.println("=== 数组的声明和初始化 ===");
int[] scores = {90, 85, 78, 92, 88}; System.out.println("scores长度:" + scores.length);
int[] scores2 = new int[]{90, 85, 78, 92, 88};
int[] ages = new int[5];
System.out.println("ages[0] 默认值:" + ages[0]);
ages[0] = 18; ages[1] = 22; System.out.println("ages[0] = " + ages[0]); }
|
| 初始化方式 |
语法 |
适用场景 |
| 静态初始化 |
int[] arr = {1, 2, 3}; |
已知所有元素值 |
| 动态初始化 |
int[] arr = new int[5]; |
只知道长度,值后面再填 |
注意:int[] arr 和 int arr[] 都行,但 Java 推荐前者,因为”int[]”整体表示”整型数组”这个类型,更清晰
访问元素和遍历
下标从0开始,最大下标是 length - 1,这跟我们日常从1开始数不一样,刚开始很容易搞错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Test public void testArrayAccess() { System.out.println("=== 访问和遍历 ===");
String[] fruits = {"苹果", "香蕉", "橘子", "葡萄"};
System.out.println("第1个水果:" + fruits[0]); System.out.println("第4个水果:" + fruits[3]);
System.out.println("--- for循环遍历 ---"); for (int i = 0; i < fruits.length; i++) { System.out.println("下标" + i + ":" + fruits[i]); }
System.out.println("--- for-each遍历 ---"); for (String fruit : fruits) { System.out.println(fruit); } }
|
| 遍历方式 |
语法 |
优点 |
缺点 |
| for循环 |
for (int i = 0; i < arr.length; i++) |
能拿到下标,能修改元素 |
写起来稍长 |
| for-each |
for (int x : arr) |
简洁,不会越界 |
拿不到下标,不能修改元素 |
常见异常:ArrayIndexOutOfBoundsException
数组越界是新手最常踩的坑,就是你去开编号不存在的柜子,Java直接报错给你看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Test public void testArrayException() { System.out.println("=== 数组越界异常 ===");
int[] arr = {10, 20, 30};
int index = 3; if (index >= 0 && index < arr.length) { System.out.println(arr[index]); } else { System.out.println("下标 " + index + " 越界了!数组长度只有 " + arr.length); } }
|
常见踩坑场景
循环条件写成 i <= arr.length,应该是 i < arr.length
以为最大下标等于长度,实际最大下标是 length - 1
数组长度为0时直接访问 arr[0]
多维数组
二维数组就是”数组的数组”,可以想象成一张 Excel 表格:行和列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @Test public void testMultiArray() { System.out.println("=== 二维数组 ===");
int[][] scores = { {90, 85, 78, 92}, {88, 76, 95, 80}, {70, 82, 89, 91} };
System.out.println("同学B的第3科成绩:" + scores[1][2]);
for (int i = 0; i < scores.length; i++) { System.out.print("同学" + i + "的成绩:"); for (int j = 0; j < scores[i].length; j++) { System.out.print(scores[i][j] + " "); } System.out.println(); }
int[][] matrix = new int[3][4]; matrix[0][0] = 1; matrix[1][1] = 1; matrix[2][2] = 1;
}
|
理解方式:int[][] arr 就是”int数组”的数组。arr[i] 拿到的是第 i 行(本身也是一个数组),arr[i][j] 才拿到具体那个元素
Arrays 工具类
Java 自带的数组工具箱,帮你干排序、打印、复制这些脏活累活
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Test public void testArraysUtil() { System.out.println("=== Arrays 工具类 ===");
int[] arr = {5, 2, 8, 1, 9, 3};
System.out.println(Arrays.toString(arr));
Arrays.sort(arr); System.out.println("排序后:" + Arrays.toString(arr));
int[] copy = Arrays.copyOf(arr, 3); System.out.println("复制前3个:" + Arrays.toString(copy));
int[] expanded = Arrays.copyOf(arr, 10); System.out.println("扩容到10:" + Arrays.toString(expanded));
int[] filled = new int[5]; Arrays.fill(filled, 666); System.out.println("fill后:" + Arrays.toString(filled));
int[] a = {1, 2, 3}; int[] b = {1, 2, 3}; System.out.println("a == b:" + (a == b)); System.out.println("Arrays.equals:" + Arrays.equals(a, b)); }
|
| 方法 |
作用 |
示例 |
Arrays.toString(arr) |
打印数组内容 |
[1, 2, 3] |
Arrays.sort(arr) |
排序(升序) |
[1, 2, 3, 5, 8, 9] |
Arrays.copyOf(arr, len) |
复制/扩容 |
复制前N个或扩容 |
Arrays.fill(arr, val) |
填充 |
全部填成指定值 |
Arrays.equals(a, b) |
比较内容 |
不比地址比内容 |
数组 vs ArrayList 预告
数组长度固定是它最大的限制。实际开发中,更常用的是 ArrayList,它是可以自动扩容的”高级数组”
| 对比项 |
数组 |
ArrayList |
| 长度 |
固定,创建时确定 |
动态增长,自动扩容 |
| 类型 |
基本类型和引用类型都行 |
只能放引用类型(int要用Integer) |
| 语法 |
arr[i] |
list.get(i) |
| 增删元素 |
不支持(长度固定) |
add()、remove() 随便搞 |
| 性能 |
稍快(直接内存访问) |
稍慢(有封装开销) |
结论:知道长度用数组,不知道用ArrayList。后面学集合框架的时候会详细讲
常见坑总结
| 坑 |
说明 |
正确做法 |
| 下标从0开始 |
arr[1] 是第二个元素,不是第一个 |
时刻记住:第N个元素的下标是N-1 |
| 越界异常 |
访问不存在的下标直接崩 |
循环用 i < arr.length,别用 <= |
| 直接打印数组 |
System.out.println(arr) 输出的是地址 |
用 Arrays.toString(arr) |
| 数组比较 |
== 比较的是地址不是内容 |
用 Arrays.equals(a, b) |
| 长度是属性不是方法 |
arr.length 没有括号 |
字符串才是 str.length() 有括号 |
练习题
题1:反转数组
把 {1, 2, 3, 4, 5} 变成 {5, 4, 3, 2, 1},不能用新数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Test public void testReverse() { int[] arr = {1, 2, 3, 4, 5};
int left = 0, right = arr.length - 1; while (left < right) { int temp = arr[left]; arr[left] = arr[right]; arr[right] = temp; left++; right--; }
System.out.println(Arrays.toString(arr)); }
|
题2:找最大值
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Test public void testFindMax() { int[] arr = {34, 78, 12, 95, 56, 23};
int max = arr[0]; for (int i = 1; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } }
System.out.println("最大值:" + max); }
|
题3:冒泡排序
排序的经典入门算法,像水里的泡泡一样,大的数一轮一轮往后”冒”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Test public void testBubbleSort() { int[] arr = {5, 3, 8, 1, 2};
for (int i = 0; i < arr.length - 1; i++) { for (int j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } }
System.out.println("排序后:" + Arrays.toString(arr)); }
|
冒泡排序时间复杂度是 O(n^2),效率不高,实际开发直接用 Arrays.sort() 就行,但面试经常考手写