Skip to content

第4课:数组

🎯 学习目标

  • 掌握一维数组的创建和使用
  • 掌握多维数组
  • 理解数组的内存结构
  • 掌握 Arrays 工具类

📖 一、一维数组

C vs Java 数组

C 语言

c
// C: 数组大小必须是常量
int arr[5];
arr[0] = 10;

// 动态数组需要手动管理
int* arr2 = malloc(5 * sizeof(int));
free(arr2);

Java

java
// Java: 数组大小可以是变量
int size = 5;
int[] arr = new int[size];
arr[0] = 10;

// 自动垃圾回收,无需手动释放

1. 数组声明和创建

java
// 方式1:声明 + 创建
int[] numbers;           // 声明
numbers = new int[5];    // 创建

// 方式2:合并
int[] numbers = new int[5];

// 方式3:声明时初始化
int[] numbers = {1, 2, 3, 4, 5};

// 方式4:new + 初始化
int[] numbers = new int[]{1, 2, 3, 4, 5};

// 注意:Java 推荐 int[] arr,而不是 int arr[]

2. 数组访问

java
int[] arr = {10, 20, 30, 40, 50};

// 访问元素
int first = arr[0];   // 10
int last = arr[4];    // 50

// 修改元素
arr[0] = 100;

// 获取长度
int length = arr.length;  // 5(属性,不是方法)

// 遍历
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

// 增强 for 循环
for (int num : arr) {
    System.out.println(num);
}

3. 数组默认值

java
int[] intArr = new int[3];       // [0, 0, 0]
double[] doubleArr = new double[3]; // [0.0, 0.0, 0.0]
boolean[] boolArr = new boolean[3];  // [false, false, false]
String[] strArr = new String[3];     // [null, null, null]

4. 数组越界

java
int[] arr = {1, 2, 3};
System.out.println(arr[5]);  // ❌ ArrayIndexOutOfBoundsException

// Java 会检查越界(比 C 安全)

📖 二、多维数组

1. 二维数组

java
// 声明和创建
int[][] matrix = new int[3][4];  // 3行4列

// 初始化
int[][] matrix = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

// 访问元素
int element = matrix[1][2];  // 7

// 遍历
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();
}

// 增强 for
for (int[] row : matrix) {
    for (int num : row) {
        System.out.print(num + " ");
    }
    System.out.println();
}

2. 不规则数组

java
// Java 支持不规则数组(每行长度不同)
int[][] irregular = new int[3][];
irregular[0] = new int[2];
irregular[1] = new int[3];
irregular[2] = new int[4];

// 或
int[][] irregular = {
    {1, 2},
    {3, 4, 5},
    {6, 7, 8, 9}
};

📖 三、数组操作

1. 数组复制

java
int[] source = {1, 2, 3, 4, 5};

// 方式1:循环复制
int[] copy1 = new int[source.length];
for (int i = 0; i < source.length; i++) {
    copy1[i] = source[i];
}

// 方式2:System.arraycopy
int[] copy2 = new int[source.length];
System.arraycopy(source, 0, copy2, 0, source.length);

// 方式3:Arrays.copyOf
int[] copy3 = Arrays.copyOf(source, source.length);

// 方式4:clone(浅拷贝)
int[] copy4 = source.clone();

2. 数组排序

java
int[] arr = {5, 2, 8, 1, 9};

// 排序
Arrays.sort(arr);  // [1, 2, 5, 8, 9]

// 部分排序
Arrays.sort(arr, 1, 4);  // 只排序索引1-3

// 降序(需要包装类)
Integer[] arr2 = {5, 2, 8, 1, 9};
Arrays.sort(arr2, Collections.reverseOrder());

3. 数组查找

java
int[] arr = {1, 2, 5, 8, 9};

// 二分查找(数组必须已排序)
int index = Arrays.binarySearch(arr, 5);  // 2
int index2 = Arrays.binarySearch(arr, 6); // -4(负数表示未找到)

// 线性查找(自己实现)
public static int linearSearch(int[] arr, int target) {
    for (int i = 0; i < arr.length; i++) {
        if (arr[i] == target) {
            return i;
        }
    }
    return -1;
}

4. 数组填充

java
int[] arr = new int[5];

// 填充整个数组
Arrays.fill(arr, 10);  // [10, 10, 10, 10, 10]

// 填充部分
Arrays.fill(arr, 1, 4, 20);  // [10, 20, 20, 20, 10]

5. 数组比较

java
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
int[] arr3 = {1, 2, 4};

// == 比较引用
System.out.println(arr1 == arr2);  // false

// Arrays.equals 比较内容
System.out.println(Arrays.equals(arr1, arr2));  // true
System.out.println(Arrays.equals(arr1, arr3));  // false

📖 四、Arrays 工具类

java
import java.util.Arrays;

int[] arr = {5, 2, 8, 1, 9};

// 排序
Arrays.sort(arr);

// 转字符串
String str = Arrays.toString(arr);  // "[1, 2, 5, 8, 9]"

// 二分查找
int index = Arrays.binarySearch(arr, 5);

// 填充
Arrays.fill(arr, 0);

// 复制
int[] copy = Arrays.copyOf(arr, arr.length);

// 复制范围
int[] copy2 = Arrays.copyOfRange(arr, 1, 4);

// 比较
boolean equal = Arrays.equals(arr, copy);

// 转 List
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

📖 五、常见应用

1. 数组反转

java
public static void reverse(int[] arr) {
    int left = 0, right = arr.length - 1;
    while (left < right) {
        int temp = arr[left];
        arr[left] = arr[right];
        arr[right] = temp;
        left++;
        right--;
    }
}

2. 最大值和最小值

java
public static int max(int[] arr) {
    int max = arr[0];
    for (int i = 1; i < arr.length; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    return max;
}

public static int min(int[] arr) {
    int min = arr[0];
    for (int i = 1; i < arr.length; i++) {
        if (arr[i] < min) {
            min = arr[i];
        }
    }
    return min;
}

3. 数组求和与平均值

java
public static int sum(int[] arr) {
    int sum = 0;
    for (int num : arr) {
        sum += num;
    }
    return sum;
}

public static double average(int[] arr) {
    return (double) sum(arr) / arr.length;
}

4. 冒泡排序

java
public static void bubbleSort(int[] arr) {
    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;
            }
        }
    }
}

⚠️ 常见陷阱

陷阱1:数组越界

数组下标从 0 开始,最后一个元素下标是 length - 1。访问 arr[arr.length] 会抛出 ArrayIndexOutOfBoundsException

陷阱2:混淆 length 和 length()

数组长度是属性 arr.length,字符串长度是方法 str.length()

陷阱3:二维数组不一定是矩形

Java 二维数组本质是“数组的数组”,每一行长度可以不同。

陷阱4:数组拷贝只复制引用

对象数组复制后,数组是新的,但里面的对象引用仍指向同一批对象。


🆚 Java vs C 对比

特性C 数组Java 数组
越界检查无,可能破坏内存有,抛运行时异常
长度信息通常需要额外传入自带 length
多维数组连续内存或指针数组数组的数组
初始化未初始化可能是垃圾值有默认值

对 C 程序员来说,Java 数组更安全,但也更受约束。它保留长度、检查越界,并且数组本身是对象。


💡 最佳实践

  1. 检查数组长度
java
if (arr != null && arr.length > 0) {
    // 安全访问
}
  1. 使用增强 for 循环
java
// ✅ 推荐
for (int num : arr) {
    System.out.println(num);
}

// ❌ 不推荐(如果不需要索引)
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}
  1. 使用 Arrays 工具类
java
// ✅ 推荐
Arrays.sort(arr);
Arrays.toString(arr);

// ❌ 不推荐:自己实现

📝 练习

完成 练习/Ex04_Arrays.java

  1. 数组基本操作
  2. 数组排序和查找
  3. 多维数组
  4. 数组算法实现
  5. Arrays 工具类使用
  6. 综合:成绩管理系统

🎓 下一步

  • 第5课:方法 - 方法定义、参数传递、递归