Skip to content

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));   // true

8. 实战练习

文件内容
ArrayBasics.java数组基础操作
Array2DDemo.java二维数组与矩阵
SortAndSearch.java排序与二分查找
StudentScores.java学生成绩统计实战

9. 浏览器演示

打开 demo.html 可视化体验:

  • 数组创建与修改
  • 排序算法动画
  • 二分查找过程

10. 面试可能会问什么?

Q1: 数组和集合的区别?

对比数组集合(List/Set/Map)
长度固定可变
类型同一种可存任意对象
基本类型直接存自动装箱(如 List<Integer>
方法少(仅 length)丰富
线程安全部分有线程安全实现

Q2: int[] aint a[] 的区别?

:完全相同。前者是 Java 推荐写法(类型在一起更清晰),后者兼容 C/C++ 风格。

Q3: lengthlength() 的区别?

  • 数组: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 必会
✅ 数组是引用类型,方法里能改原数组

🔗 导航