主题
Chapter 05 · 数组
数组是装一堆同类型数据的"火车",每节车厢都有自己的编号。
🎯 本章目标
- 理解 数组 的概念与内存模型
- 学会 声明、创建、访问、遍历 一维数组
- 掌握 二维数组 的处理方式
- 熟练使用
Arrays工具类 - 能写出常见 排序、查找、统计 算法
1. 数组是什么?火车的故事
把数组想象成一列火车:
- 整列火车叫 数组(如
int[] nums) - 每节车厢叫 元素
- 每节车厢有 编号 0、1、2...(叫"下标"或"索引")
- 所有车厢只能装 同一种东西(同类型)
- 一旦造好,车厢数量不能变(数组长度固定)
2. 创建数组的三种方式
java
int[] a = new int[5]; // 方式 1:先开 5 节空车厢(默认值 0)
int[] b = {1, 2, 3, 4, 5}; // 方式 2:直接装好(推荐)
int[] c = new int[]{1, 2, 3, 4, 5}; // 方式 3:等价于方式 2
System.out.println(a[0]); // 0(默认值)
System.out.println(b[2]); // 3(下标 2,第 3 个元素)
System.out.println(b.length); // 5(数组长度,注意是属性不是方法)💡 下标从 0 开始!
b.length是 5,但合法下标是 0~4。访问b[5]会抛ArrayIndexOutOfBoundsException。
各种类型的默认值
| 数组类型 | 默认值 |
|---|---|
int[]、long[] | 0 |
double[]、float[] | 0.0 |
char[] | \u0000 |
boolean[] | false |
引用类型数组(如 String[]) | null |
3. 遍历数组的三种方式
java
int[] nums = {10, 20, 30, 40, 50};
// 方式 1:普通 for(拿得到下标)
for (int i = 0; i < nums.length; i++) {
System.out.println("下标 " + i + " = " + nums[i]);
}
// 方式 2:for-each(更简洁,但拿不到下标)
for (int n : nums) {
System.out.println(n);
}
// 方式 3:Arrays.toString 一键打印
System.out.println(Arrays.toString(nums)); // [10, 20, 30, 40, 50]🚫 千万别这样:直接
System.out.println(nums),输出的是[I@7a81197d这种鬼东西(数组的 hashCode 字符串)。
4. 二维数组:表格
二维数组就是 "数组的数组",本质是行列结构,常用于表示矩阵、棋盘、表格。
java
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println(matrix[1][2]); // 6(第 2 行第 3 列)
System.out.println(matrix.length); // 3(行数)
System.out.println(matrix[0].length); // 3(第 0 行的列数)
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}5. Arrays 工具类(必会)
java.util.Arrays 是 JDK 自带的数组瑞士军刀:
java
import java.util.Arrays;
int[] nums = {3, 1, 4, 1, 5, 9, 2, 6};
Arrays.toString(nums); // 转字符串: "[3, 1, 4, 1, 5, 9, 2, 6]"
Arrays.sort(nums); // 原地排序: [1, 1, 2, 3, 4, 5, 6, 9]
Arrays.binarySearch(nums, 5); // 二分查找下标(前提:已排序)
Arrays.fill(nums, 0); // 全部填充为 0
int[] copy = Arrays.copyOf(nums, 10); // 复制并扩容到长度 10
int[] sub = Arrays.copyOfRange(nums, 2, 5); // 截取 [2, 5)
boolean eq = Arrays.equals(arr1, arr2); // 比较两数组内容⚠️ 数组比较坑:用
==比的是引用,必须用Arrays.equals()比内容。
6. 经典算法实战
6.1 求和、平均、最大、最小
java
int[] nums = {3, 1, 4, 1, 5, 9, 2, 6};
int sum = 0;
int max = nums[0], min = nums[0];
for (int n : nums) {
sum += n;
if (n > max) max = n;
if (n < min) min = n;
}
double avg = (double) sum / nums.length;6.2 反转数组
java
int[] nums = {1, 2, 3, 4, 5};
for (int i = 0, j = nums.length - 1; i < j; i++, j--) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
// 结果: [5, 4, 3, 2, 1]6.3 冒泡排序(理解原理用)
java
int[] nums = {3, 1, 4, 1, 5};
for (int i = 0; i < nums.length - 1; i++) {
for (int j = 0; j < nums.length - 1 - i; j++) {
if (nums[j] > nums[j+1]) {
int t = nums[j]; nums[j] = nums[j+1]; nums[j+1] = t;
}
}
}💡 实际开发用
Arrays.sort()(底层是双轴快排,比冒泡快几十倍)。
6.4 二分查找(前提:已排序)
java
int[] sorted = {1, 3, 5, 7, 9, 11, 13};
int target = 7;
int low = 0, high = sorted.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (sorted[mid] == target) { /* 找到,下标 mid */ break; }
else if (sorted[mid] < target) low = mid + 1;
else high = mid - 1;
}7. 数组的常见坑
坑 1:越界
java
int[] a = new int[3];
a[3] = 100; // ❌ ArrayIndexOutOfBoundsException坑 2:长度不可变
java
int[] a = new int[3];
a.length = 5; // ❌ 编译错误
// 想要"扩容"只能新建数组并复制坑 3:值传递?引用传递?
java
void modify(int[] arr) { arr[0] = 999; }
int[] nums = {1, 2, 3};
modify(nums);
System.out.println(nums[0]); // 999!💡 数组是 引用类型,方法里改的就是原数组(与基本类型不同)。
坑 4:== 不是比内容
java
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(a == b); // false
System.out.println(Arrays.equals(a, b)); // true8. 实战练习
| 文件 | 内容 |
|---|---|
ArrayBasics.java | 数组基础操作 |
Array2DDemo.java | 二维数组与矩阵 |
SortAndSearch.java | 排序与二分查找 |
StudentScores.java | 学生成绩统计实战 |
9. 浏览器演示
打开 demo.html 可视化体验:
- 数组创建与修改
- 排序算法动画
- 二分查找过程
10. 面试可能会问什么?
Q1: 数组和集合的区别?
| 对比 | 数组 | 集合(List/Set/Map) |
|---|---|---|
| 长度 | 固定 | 可变 |
| 类型 | 同一种 | 可存任意对象 |
| 基本类型 | 直接存 | 自动装箱(如 List<Integer>) |
| 方法 | 少(仅 length) | 丰富 |
| 线程安全 | 否 | 部分有线程安全实现 |
Q2: int[] a 和 int a[] 的区别?
答:完全相同。前者是 Java 推荐写法(类型在一起更清晰),后者兼容 C/C++ 风格。
Q3: length 和 length() 的区别?
- 数组:
arr.length(属性,不带括号) - String:
s.length()(方法,带括号) - List:
list.size()(方法)
Q4: 怎么实现"动态扩容"的数组?
答:用 ArrayList,或者手动:
java
int[] newArr = Arrays.copyOf(oldArr, oldArr.length * 2);ArrayList 内部就是这么干的,扩容因子 1.5。
Q5: 二维数组每行长度可以不同吗?
答:可以!这叫 锯齿数组:
java
int[][] jagged = new int[3][];
jagged[0] = new int[]{1};
jagged[1] = new int[]{1, 2};
jagged[2] = new int[]{1, 2, 3};Q6: 为什么 Arrays.asList(intArray) 拿到的是 List<int[]>?
答:因为 int[] 是 基本类型数组,整个被当成一个元素。要拿到 List<Integer> 必须用 Integer[] 或 JDK 8+ 的 Stream:
java
int[] arr = {1, 2, 3};
List<Integer> list = Arrays.stream(arr).boxed().toList();🎁 本章小结
✅ 数组是同类型元素的有序容器,长度不可变
✅ 下标从 0 开始,arr.length 是属性(不是方法)
✅ 二维数组是数组的数组,每行长度可不同
✅ Arrays 工具类:toString / sort / equals / copyOf 必会
✅ 数组是引用类型,方法里能改原数组🔗 导航
- ⬅️ 上一章:Chapter 04 · 流程控制
- ➡️ 下一章:Chapter 06 · 方法(函数)