引言:什么是数组?
数组是一种固定长度的、用于存储相同类型元素的数据结构。可以把数组想象成一排连续的储物柜,每个储物柜都有编号(索引),我们可以通过编号快速访问任意一个储物柜。
数组类比
数组就像一排连续的储物柜:
┌─────┬─────┬─────┬─────┬─────┐
│ 柜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 在线代码编辑器
📝 运行结果