数组
1. 数组的基本使用¶
数组是 Java 中一种基本的数据结构,用于存储**固定大小**的、**相同类型**元素的集合。
1.1 声明数组¶
有两种声明方式,推荐使用第一种,因为它更清晰地将类型 int[] 和变量名分开。
// 推荐方式
int[] anArray;
// C/C++ 风格,在Java中不推荐
int anArray[];
1.2 初始化数组¶
声明数组后,必须对其进行初始化才能使用。初始化意味着在内存中为数组分配空间。
a. 静态初始化¶
在声明的同时指定数组的内容。编译器会自动计算数组的长度。
int[] staticArray = {10, 20, 30, 40, 50};
String[] stringArray = {"Hello", "World", "Java"};
b. 动态初始化¶
使用 new 关键字指定数组的类型和长度。数组元素会根据其类型被赋予默认值。
- 整数类型(
byte,short,int,long):0 - 浮点类型(
float,double):0.0 - 字符类型(
char):\u0000 - 布尔类型(
boolean):false - 引用类型(如
String):null
// 创建一个长度为 5 的整数数组,所有元素默认为 0
int[] dynamicArray = new int[5];
// 创建一个长度为 3 的字符串数组,所有元素默认为 null
String[] names = new String[3];
1.3 访问和修改数组元素¶
通过**索引**(下标)来访问或修改数组元素。索引从 0 开始,最大到 数组长度 - 1。
int[] myArray = {10, 20, 30};
// 访问第一个元素 (索引为 0)
System.out.println(myArray[0]); // 输出: 10
// 修改第二个元素 (索引为 1)
myArray[1] = 99;
System.out.println(myArray[1]); // 输出: 99
// 获取数组长度
System.out.println(myArray.length); // 输出: 3
注意:如果尝试访问超出范围的索引(如
myArray[3]),程序将抛出ArrayIndexOutOfBoundsException异常。
1.4 遍历数组¶
a. 使用 for 循环¶
int[] numbers = {1, 2, 3, 4, 5};
for (int i = 0; i < numbers.length; i++) {
System.out.println("Element at index " + i + ": " + numbers[i]);
}
b. 使用 for-each 循环(增强 for 循环)¶
这种方式更简洁,但不提供索引信息。
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
System.out.println(number);
}
2. 数组的赋值机制(引用传递)¶
Java 中的数组属于**引用类型**。当你将一个数组变量赋值给另一个数组变量时,你传递的是数组的**内存地址引用**,而不是数组内容的副本。
这意味着两个变量将指向**同一个**内存中的数组对象。对其中一个变量所做的修改会影响到另一个变量。
int[] arrA = {10, 20, 30};
int[] arrB = arrA; // 将 arrA 的地址引用赋给 arrB
System.out.println("arrA[1] before change: " + arrA[1]); // 输出: 20
System.out.println("arrB[1] before change: " + arrB[1]); // 输出: 20
// 修改 arrB 的第二个元素
arrB[1] = 99;
System.out.println("arrA[1] after change: " + arrA[1]); // 输出: 99
System.out.println("arrB[1] after change: " + arrB[1]); // 输出: 99
3. 数组的拷贝¶
如果想创建一个内容完全相同但独立的新数组,就需要进行拷贝。
a. 手动拷贝(for 循环)¶
这是最基本的方法,通过遍历旧数组并将每个元素赋给新数组的对应位置。
int[] source = {1, 2, 3};
int[] destination = new int[source.length]; // 创建一个同样大小的新数组
for (int i = 0; i < source.length; i++) {
destination[i] = source[i];
}
destination[0] = 100; // 修改新数组,不会影响原数组
System.out.println(source[0]); // 输出: 1
System.out.println(destination[0]); // 输出: 100
b. 使用 System.arraycopy()¶
这是一个高效的底层方法,适合大规模数组的拷贝。
// public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
// src: 源数组
// srcPos: 源数组的起始位置
// dest: 目标数组
// destPos: 目标数组的起始位置
// length: 要拷贝的元素数量
int[] source = {10, 20, 30, 40, 50};
int[] destination = new int[5];
System.arraycopy(source, 0, destination, 0, source.length);
c. 使用 Arrays.copyOf()¶
java.util.Arrays 类提供的方法,它会返回一个新数组,是拷贝最简洁的方式。
import java.util.Arrays;
int[] source = {1, 2, 3};
int[] destination = Arrays.copyOf(source, source.length);
4. 数组的翻转¶
翻转是指将数组中的元素顺序颠倒,例如 [1, 2, 3] 翻转后变为 [3, 2, 1]。
常用的方法是使用**双指针法**:一个指针从头开始,另一个指针从尾开始,交换它们指向的元素,然后两个指针向中间移动,直到相遇。
int[] arr = {10, 20, 30, 40, 50};
for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
// 交换 arr[i] 和 arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// 打印翻转后的数组
// 输出: [50, 40, 30, 20, 10]
System.out.println(Arrays.toString(arr));
5. 数组的扩容¶
数组的长度是固定的,不能直接在原有数组上增加长度。扩容的本质是创建一个更大的新数组,然后将旧数组的内容拷贝过去。
int[] oldArray = {1, 2, 3};
// 1. 创建一个更大的新数组
int[] newArray = new int[oldArray.length * 2]; // 例如,容量扩大一倍
// 2. 将旧数组的元素拷贝到新数组
for (int i = 0; i < oldArray.length; i++) {
newArray[i] = oldArray[i];
}
// 3. (可选) 将旧数组的引用指向新数组
oldArray = newArray;
// 现在 oldArray 指向了新扩容的数组
System.out.println("New length: " + oldArray.length); // 输出: 6
System.out.println(Arrays.toString(oldArray)); // 输出: [1, 2, 3, 0, 0, 0]
6. 数组的缩减¶
缩减与扩容类似,也是通过创建一个更小的新数组并拷贝所需元素来实现。通常用于删除数组中的某个元素。
例如,从数组 [10, 20, 30, 40] 中删除索引为 2 的元素 30。
int[] original = {10, 20, 30, 40};
int indexToRemove = 2;
// 1. 创建一个比原数组小 1 的新数组
int[] newArray = new int[original.length - 1];
// 2. 拷贝要删除元素之前的部分
for (int i = 0; i < indexToRemove; i++) {
newArray[i] = original[i];
}
// 3. 拷贝要删除元素之后的部分
for (int i = indexToRemove + 1; i < original.length; i++) {
newArray[i - 1] = original[i];
}
// 4. (可选) 将原数组引用指向新数组
original = newArray;
// 打印缩减后的数组
// 输出: [10, 20, 40]
System.out.println(Arrays.toString(original));
专业提示:在实际开发中,如果需要频繁地进行扩容和缩减操作,使用
ArrayList(动态数组)会更加方便和高效,因为它内部已经封装好了这些复杂的逻辑。