教程中心 / Java教程 / 数组

数组

18分钟 基础类型

引言:什么是数组?

数组是一种固定长度的、用于存储相同类型元素的数据结构。可以把数组想象成一排连续的储物柜,每个储物柜都有编号(索引),我们可以通过编号快速访问任意一个储物柜。

数组类比

数组就像一排连续的储物柜:
┌─────┬─────┬─────┬─────┬─────┐
│ 柜1 │ 柜2 │ 柜3 │ 柜4 │ 柜5 │
│索引0 │索引1 │索引2 │索引3 │索引4 │
└─────┴─────┴─────┴─────┴─────┘
  ↓     ↓     ↓     ↓     ↓
 numbers[0] numbers[1] ... 访问方式

数组的特点:

  • 数组长度固定,创建后不能改变
  • 数组元素类型相同
  • 数组元素在内存中是连续存储
  • 可以通过索引快速访问任意元素

一维数组的创建和初始化

方式1:先声明后赋值

int[] scores;          // 声明数组
scores = new int[5];    // 创建长度为5的数组
scores[0] = 90;         // 为元素赋值
scores[1] = 85;
scores[2] = 78;
scores[3] = 92;
scores[4] = 88;

方式2:声明时直接初始化

int[] scores = new int[]{90, 85, 78, 92, 88};

方式3:使用简化写法(最常用)

int[] scores = {90, 85, 78, 92, 88};

// 注意:简化写法只能在声明时直接使用
// 下面的写法是错误的:
// int[] scores;
// scores = {90, 85, 78, 92, 88};  // 编译错误!

不同类型的数组

// 整数数组
int[] intArray = {1, 2, 3, 4, 5};

// 浮点数数组
double[] doubleArray = {1.1, 2.2, 3.3};

// 字符串数组
String[] names = {"Alice", "Bob", "Charlie"};

// 布尔数组
boolean[] flags = {true, false, true};

数组的访问

通过索引访问元素

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

System.out.println(numbers[0]);  // 输出第一个元素:10
System.out.println(numbers[4]);  // 输出最后一个元素:50
System.out.println(numbers[numbers.length - 1]);  // 输出最后一个元素:50

数组长度 - length属性

int[] numbers = {10, 20, 30, 40, 50};
System.out.println("数组长度: " + numbers.length);  // 5

数组索引越界错误:数组索引从0开始,最后一个元素的索引是 length-1。如果访问超出范围的索引,会抛出 ArrayIndexOutOfBoundsException 异常!

int[] numbers = {10, 20, 30, 40, 50};
// numbers[5]  // 错误!索引5超出范围(0-4)
// numbers[-1] // 错误!索引不能为负数

数组的遍历

方式1:普通for循环

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

for (int i = 0; i < numbers.length; i++) {
    System.out.println("numbers[" + i + "] = " + numbers[i]);
}

方式2:增强for循环(foreach)

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

for (int num : numbers) {
    System.out.println("元素值: " + num);
}

对比:普通for vs 增强for

普通for循环

// 优点:可以访问索引,可以修改元素
for (int i = 0; i < arr.length; i++) {
    arr[i] = arr[i] * 2;  // 可以修改元素
    System.out.println(i);  // 可以访问索引
}

增强for循环

// 优点:语法简洁,专注于元素
// 缺点:不能访问索引,不能修改数组
for (int num : arr) {
    System.out.println(num);  // 只读元素值
}

二维数组

二维数组是"数组的数组",可以想象成表格或矩阵。

二维数组类比

二维数组就像一个表格:
        ┌───┬───┬───┐
  行0  │ 1 │ 2 │ 3 │
        ├───┼───┼───┤
  行1  │ 4 │ 5 │ 6 │
        ├───┼───┼───┤
  行2  │ 7 │ 8 │ 9 │
        └───┴───┴───┘
         列0  列1  列2

matrix[2][1] = 8  访问第3行第2列的元素

二维数组的创建

// 方式1:先声明后创建
int[][] matrix = new int[3][4];  // 3行4列

// 方式2:声明时直接初始化
int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// 方式3:创建不规则的二维数组(每一行的列数可以不同)
int[][] jagged = new int[3][];
jagged[0] = new int[2];  // 第一行2列
jagged[1] = new int[3];  // 第二行3列
jagged[2] = new int[4];  // 第三行4列

二维数组的访问

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

System.out.println(matrix[0][0]);  // 第1行第1列:1
System.out.println(matrix[1][2]);  // 第2行第3列:6
System.out.println(matrix.length);      // 3(行数)
System.out.println(matrix[0].length);   // 3(第一行的列数)

二维数组的遍历

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

// 使用嵌套循环
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();  // 每行结束后换行
}

输出:

1 2 3
4 5 6
7 8 9

Arrays类的常用方法

java.util.Arrays类提供了大量操作数组的静态方法,是处理数组的重要工具。

1. 数组转字符串 - Arrays.toString()

import java.util.Arrays;

int[] numbers = {5, 2, 8, 1, 9};
System.out.println(Arrays.toString(numbers));  // [5, 2, 8, 1, 9]

2. 数组排序 - Arrays.sort()

import java.util.Arrays;

int[] numbers = {5, 2, 8, 1, 9};
System.out.println("排序前: " + Arrays.toString(numbers));

Arrays.sort(numbers);  // 升序排序
System.out.println("排序后: " + Arrays.toString(numbers));  // [1, 2, 5, 8, 9]

// 对部分元素排序
int[] scores = {90, 85, 78, 92, 88, 95, 70};
Arrays.sort(scores, 0, 5);  // 只对索引0到4的元素排序
System.out.println("部分排序: " + Arrays.toString(scores));

// 降序排序(Java 8+)
Integer[] nums = {5, 2, 8, 1, 9};
Arrays.sort(nums, Comparator.reverseOrder());
System.out.println("降序: " + Arrays.toString(nums));

3. 数组填充 - Arrays.fill()

import java.util.Arrays;

int[] arr = new int[5];
Arrays.fill(arr, 100);  // 用100填充所有元素
System.out.println("填充后: " + Arrays.toString(arr));  // [100, 100, 100, 100, 100]

// 部分填充
Arrays.fill(arr, 1, 3, 50);  // 填充索引1到2的元素为50
System.out.println("部分填充: " + Arrays.toString(arr));  // [100, 50, 50, 100, 100]

4. 数组复制 - Arrays.copyOf()

import java.util.Arrays;

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

// 复制并指定新长度(如果新长度大于原长度,多出的部分用0填充)
int[] copied1 = Arrays.copyOf(original, 10);
System.out.println("复制并扩展: " + Arrays.toString(copied1));

// 复制指定范围
int[] copied2 = Arrays.copyOfRange(original, 1, 4);
System.out.println("复制范围[1,4): " + Arrays.toString(copied2));

5. 数组查找 - Arrays.binarySearch()

重要:使用二分查找前,数组必须先排序!否则结果不可预测。

import java.util.Arrays;

int[] numbers = {5, 2, 8, 1, 9};
Arrays.sort(numbers);  // 先排序
System.out.println("排序后: " + Arrays.toString(numbers));

int index = Arrays.binarySearch(numbers, 8);
System.out.println("元素8的索引: " + index);  // 4

int notFound = Arrays.binarySearch(numbers, 10);
System.out.println("元素10的索引: " + notFound);  // -6(负数表示未找到)

6. 判断数组相等 - Arrays.equals()

import java.util.Arrays;

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

System.out.println("arr1 equals arr2: " + Arrays.equals(arr1, arr2));  // true
System.out.println("arr1 equals arr3: " + Arrays.equals(arr1, arr3));  // false

数组的常见操作

1. 求最大值和最小值

int[] numbers = {85, 92, 78, 95, 88};

int max = numbers[0];
int min = numbers[0];

for (int i = 1; i < numbers.length; i++) {
    if (numbers[i] > max) {
        max = numbers[i];
    }
    if (numbers[i] < min) {
        min = numbers[i];
    }
}

System.out.println("最大值: " + max);  // 95
System.out.println("最小值: " + min);  // 78

2. 计算平均值

int[] numbers = {85, 92, 78, 95, 88};
int sum = 0;

for (int num : numbers) {
    sum += num;
}

double average = (double) sum / numbers.length;
System.out.println("平均值: " + average);  // 87.6

3. 数组元素反转

import java.util.Arrays;

int[] numbers = {1, 2, 3, 4, 5};
System.out.println("反转前: " + Arrays.toString(numbers));

for (int i = 0; i < numbers.length / 2; i++) {
    int temp = numbers[i];
    numbers[i] = numbers[numbers.length - 1 - i];
    numbers[numbers.length - 1 - i] = temp;
}

System.out.println("反转后: " + Arrays.toString(numbers));  // [5, 4, 3, 2, 1]

4. 元素出现次数统计

String[] fruits = {"apple", "banana", "apple", "orange", "banana", "apple"};

java.util.HashMap countMap = new java.util.HashMap<>();
for (String fruit : fruits) {
    countMap.put(fruit, countMap.getOrDefault(fruit, 0) + 1);
}

for (java.util.Map.Entry entry : countMap.entrySet()) {
    System.out.println(entry.getKey() + " 出现 " + entry.getValue() + " 次");
}

数组的内存模型

数组是引用类型

int[] arr1 = {1, 2, 3};
int[] arr2 = arr1;  // arr2和arr1指向同一个数组

arr2[0] = 100;  // 修改arr2的元素
System.out.println(Arrays.toString(arr1));  // [100, 2, 3](arr1也被修改了!)

重要:数组是引用类型,赋值给另一个变量时,两个变量指向同一个数组对象。修改其中一个会影响另一个。

值传递 vs 引用传递

int[] numbers = {1, 2, 3};
System.out.println("调用前: " + Arrays.toString(numbers));
modifyArray(numbers);
System.out.println("调用后: " + Arrays.toString(numbers));  // [100, 2, 3]

public static void modifyArray(int[] arr) {
    arr[0] = 100;  // 修改的是原数组
}

小结

  • 数组:固定长度、相同类型的元素连续存储的数据结构
  • 索引:从0开始,最后一个元素的索引是 length-1
  • 遍历方式:普通for循环(可修改元素)和增强for循环(只读)
  • 二维数组:数组的数组,可以看作表格
  • Arrays类:提供toString()、sort()、fill()、copyOf()、binarySearch()、equals()等方法
  • 数组是引用类型:赋值给另一个变量时共享同一个数组对象
  • 注意索引越界:访问超出范围的索引会抛出 ArrayIndexOutOfBoundsException
☕ Java 在线代码编辑器
📝 运行结果