目录
一、什么是数组
在Java中,数组是一种用于存储相同类型数据的有序集合,是非常基础且重要的数据结构。数组中的每个数据称为元素,可以通过索引(下标)来访问这些元素。数组的特点包括:
-
固定大小:数组的大小在创建时是固定的,之后不能改变,一旦声明了一个特定大小的数组,就无法再向其中添加或删除元素。
-
相同类型:数组中的所有元素必须具有相同的类型,不能包含不同类型的数据。
-
有序集合:数组中的元素是按顺序存储的,每个元素都有一个与之关联的索引值(从0开始),可以通过索引值来访问数组中的特定元素。
-
内存连续:在物理内存中,数组元素是连续存储的。这种连续性使得数组在访问速度上非常快,因为一旦知道了数组的第一个元素的内存地址和元素的大小,就可以很容易地计算出数组中任何其他元素的内存地址。
-
引用数据类型:在Java中,数组是一种引用数据类型,这意味着数组存储的是指向实际数据的引用,而不是数据本身。因此,当传递数组作为参数时,传递的是数组的引用,而不是数组的内容。
二、声明和创建
在Java中,数组的声明和创建是两个相关联的步骤,但也可以看作是一个整体过程。数组的声明定义了数组的类型和名称,而数组的创建则为数组分配了内存空间。
1. 数组的声明
数组的声明指定了数组的类型和名称。数组的类型由数组内元素的类型决定,而数组的名称则是一个标识符,用于在代码中引用该数组。
数组声明的语法有两种形式:
-
dataType[] arrayName;
(推荐使用) -
dataType arrayName[];
(不推荐,但可行)
int[] arrayName; // 推荐的方式
int arrayName[]; // 不推荐的方式,但也可以使用
2. 数组的创建
数组的创建为数组分配了内存空间,并可以初始化数组元素。
三、数组的初始化
在Java中,数组的初始化主要有三种方式,分别是静态初始化、动态初始化和默认初始化。
1. 静态初始化
(Static Initialization)
静态初始化是在声明数组的同时直接为数组元素赋值。这种方式下,数组的大小由编译器根据提供的元素数量自动确定。
int[] numbers = {1, 2, 3, 4, 5};
在本例中,我们创建了一个名为numbers
的整型数组,并直接通过花括号中的元素列表初始化了它。
2. 动态初始化
(Dynamic Initialization)
动态初始化是先声明数组变量,然后指定数组的大小,但此时并不立即为数组元素赋值,之后可以根据需要逐个为数组元素赋值。
int[] numbers = new int[5]; // 创建一个大小为5的整型数组
numbers[0] = 10; // 为第一个元素赋值
numbers[1] = 20; // 为第二个元素赋值
// ... 可以继续为其他元素赋值,不赋值默认为0
在本例中,我们首先声明了一个名为numbers
的整型数组变量,然后使用new int[5]
创建了一个大小为5的数组对象,并将其引用赋值给了numbers
变量。之后,我们为数组的前两个元素赋值,其他元素保持默认的整数值0。
3. 默认初始化
(Default Initialization)
默认初始化是Java编译器在创建数组对象时自动进行的初始化。对于基本数据类型(如int
、char
、boolean
等),默认初始化的值是它们对应的零值(例如,int
类型的默认值是0
,char
类型的默认值是'\u0000'
(空字符),boolean
类型的默认值是false
);对于引用数据类型(如对象数组),默认初始化的值是null
。
当我们使用动态初始化创建一个数组但没有显式地为数组元素赋值时,就会进行默认初始化。
boolean[] flags = new boolean[3]; // 创建一个大小为3的布尔型数组,所有元素默认初始化为false
String[] names = new String[5]; // 创建一个大小为5的字符串数组,所有元素默认初始化为null
在本例中,flags
数组的所有元素都被初始化为false
,而names
数组的所有元素都被初始化为null
。
四、数组的基本使用
1. 访问元素
通过索引(从0开始)可以访问数组中的元素。
public class Demo {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
int firstNumber = numbers[0]; // 访问并获取第一个元素的值
System.out.println(firstNumber); // 输出:1
}
}
2. 数组长度
每个数组都有一个length
属性,表示数组中元素的数量。这是一个只读属性,不能修改。
public class Demo {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
int length = numbers.length; // 获取数组的长度
System.out.println(length); // 输出:5
}
}
3. 遍历数组
我们有时需要遍历数组以访问其所有元素,这可以通过使用for
循环、while
循环等来完成。
public class Demo {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
for (int i = 0; i < numbers.length; i++) { //打印每个元素的值
System.out.print(numbers[i]); //输出:12345
}
}
}
或者使用增强的for
循环(也称为“for-each”循环)遍历。
public class Demo {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) { //打印每个元素的值
System.out.print(number); //输出:12345
}
}
}
五、数组下标越界
数组下标越界(ArrayIndexOutOfBoundsException
)是指在访问数组元素时,使用的索引超出了数组的有效范围。数组的有效索引范围是从0到数组长度减1。如果尝试访问超出这个范围的索引,就会抛出ArrayIndexOutOfBoundsException
异常,导致程序崩溃或产生未预期的结果。
1. 常见原因
-
访问不存在的索引:
int[] arr = new int[5]; System.out.println(arr[5]); // 抛出ArrayIndexOutOfBoundsException,因为索引5超出了范围(有效索引为0到4)
-
循环过程中越界:
int[] arr = new int[5]; for (int i = 0; i <= arr.length; i++) { // 循环条件应该是i < arr.length System.out.println(arr[i]); }
-
使用负数索引:
int[] arr = new int[5]; System.out.println(arr[-1]); // 抛出ArrayIndexOutOfBoundsException,因为索引不能为负数
-
数组复制操作不当:
int[] source = new int[3]; int[] target = new int[2]; System.arraycopy(source, 0, target, 0, 3); // 尝试复制3个元素到只有2个元素的目标数组,抛出ArrayIndexOutOfBoundsException
2. 解决方法
-
检查索引范围: 在访问数组元素之前,确保索引在有效范围内(0到数组长度减1)。
import java.util.Scanner; public class Demo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int index = sc.nextInt(); // 接收索引值 int[] numbers = {1, 2, 3, 4, 5}; if (index >= 0 && index < numbers.length) { // 判断是否越界 System.out.println(numbers[index]); // 不越界则输出数组中索引对应的值 } else { System.out.println("索引越界"); // 越界提示 } sc.close(); } }
-
正确设置循环条件: 确保循环条件正确,不会访问超出数组长度的索引。
public class Demo { public static void main(String[] args) { int[] numbers = {1, 2, 3, 4, 5}; for (int i = 0; i < numbers.length; i++) { System.out.println(numbers[i]); } } }
-
使用try-catch块捕获异常:
可以使用try-catch块来捕获ArrayIndexOutOfBoundsException异常。如果检测到下标越界,可以提供一个自定义的错误消息或进行其他处理。
import java.util.Scanner; public class Demo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int index = sc.nextInt(); // 接收索引值 int[] numbers = {1, 2, 3, 4, 5}; try { int element = numbers[index]; } catch (ArrayIndexOutOfBoundsException e) { System.out.println("数组下标越界:" + e.getMessage()); // 越界则输出:数组下标越界:Index ? out of bounds for length 5 } sc.close(); } }
六、多维数组
在Java中,多维数组可以看作数组的数组,即数组的每一个元素本身也是一个数组。多维数组可以是二维数组、三维数组,甚至更高维度的数组,但最常见的是二维数组。
1. 二维数组
二维数组可以看作是一个表格,其中每个元素都可以通过两个索引(行和列)来访问。二维数组通常用于表示表格数据,如矩阵。
(1)声明和初始化二维数组
-
静态初始化:在声明数组的同时直接给出数组的元素。
int[][] array = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
-
动态初始化:先声明数组,再为数组的每个元素分配内存。
int[][] array = new int[3][3];
// 可以逐个赋值
array[0][0] = 1;
array[0][1] = 2;
array[0][2] = 3;
或者使用循环赋值
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
array[i][j] = i * 3 + j + 1;
}
}
(2)访问二维数组的元素
通过两个索引(行索引和列索引)来访问二维数组的元素。
public class Demo {
public static void main(String[] args) {
int[][] array = new int[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
array[i][j] = i * 3 + j + 1;
}
}
int value = array[1][2]; // 获取第二行第三列的值
System.out.println(value); // 输出:6
}
}
2. 三维数组及更高维度数组
三维数组及更高维度的数组也可以类似地声明和初始化。它们通常用于表示更复杂的数据结构,如三维空间中的点、立方体等。
使用示例:
public class Demo {
public static void main(String[] args) {
int[][][] array3D = new int[3][3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
array3D[i][j][k] = i + j + k;
}
}
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
System.out.print(array3D[i][j][k] + " ");
}
System.out.print("\t");
}
System.out.println();
}
}
}
//输出:
//0 1 2 1 2 3 2 3 4
//1 2 3 2 3 4 3 4 5
//2 3 4 3 4 5 4 5 6
七、Arrays类
Java中的Arrays
类是一个工具类,位于java.util
包下( import java.util.Arrays; ),提供了多种静态方法用于操作数组,包括排序、搜索、填充、比较等操作。由于Arrays
类的构造方法是私有的,因此不能创建对象,所有方法都是静态的,可以直接通过类名调用。
1. 一维数组常用方法
-
toString(array)
:返回数组的字符串形式。import java.util.Arrays; public class Demo { public static void main(String[] args) { int[] array = {1, 2, 3, 4, 5}; System.out.println(Arrays.toString(array)); // 输出: [1, 2, 3, 4, 5] } }
-
sort(array)
:对数组进行排序,支持升序和降序(默认升序)。可以对int
、double
、char
等基本类型数组进行排序,也可以对对象数组进行排序(对象必须实现Comparable
接口)。import java.util.Arrays; public class Demo { public static void main(String[] args) { int[] array = {5, 3, 1, 4, 2}; Arrays.sort(array); System.out.println(Arrays.toString(array)); // 输出: [1, 2, 3, 4, 5] } }
-
binarySearch(array, key)
:使用二分查找算法在排序后的数组中查找元素的位置,注意要求数组必须是有序的。import java.util.Arrays; public class Demo { public static void main(String[] args) { int[] array = {1, 2, 3, 4, 5}; int index = Arrays.binarySearch(array, 3); System.out.println(index); // 输出: 2 } }
-
equals(array1, array2)
:比较两个数组元素内容是否完全一致。import java.util.Arrays; public class Demo { public static void main(String[] args) { int[] array1 = {1, 2, 3, 4, 5}; int[] array2 = {1, 2, 3, 4, 5}; boolean isEqual = Arrays.equals(array1, array2); System.out.println(isEqual); // 输出: true } }
-
fill(array, value)
:用指定的值填充数组的每个元素。import java.util.Arrays; public class Demo { public static void main(String[] args) { int[] array = new int[5]; Arrays.fill(array, 6); System.out.println(Arrays.toString(array)); // 输出: [6, 6, 6, 6, 6] } }
2. 多维数组常用方法
-
deepToString(array)
:将多维数组转换为字符串表示。import java.util.Arrays; public class Demo { public static void main(String[] args) { int[][] array = {{1, 2, 3}, {4, 5, 6}}; System.out.println(Arrays.deepToString(array)); // 输出: [[1, 2, 3], [4, 5, 6]] } }
-
deepEquals(array1, array2)
:比较两个多维数组是否相等。import java.util.Arrays; public class Demo { public static void main(String[] args) { int[][] array1 = {{1, 2, 3}, {4, 5, 6}}; int[][] array2 = {{1, 2, 3}, {4, 5, 6}}; boolean isEqual = Arrays.deepEquals(array1, array2); System.out.println(isEqual); // 输出: true } }
3. 注意事项
-
Arrays
类中的方法都是静态的,因此可以直接通过类名调用,而不需要创建Arrays
类的实例。 -
在使用
Arrays
类的方法时,如果数组引用为空,将抛出NullPointerException
。 -
对于基本类型数组和对象数组,
Arrays
类提供了相应的方法来处理。对于对象数组,如果要对数组进行排序或比较,对象必须实现Comparable
接口,或者提供一个Comparator
来指定排序或比较的规则。
七、稀疏数组
在Java中,稀疏数组(Sparse Array)是一种用于存储具有大量零值(或某个特定值)的多维数组的数据结构。稀疏数组通过仅存储非零值(或非特定值)及其位置信息,从而节省存储空间和内存。通常,一个二维数组可以用一个三元组(row, col, value)来表示非零元素,其中row
和col
分别表示非零元素在二维数组中的行和列索引,value
表示该位置的值。所有这些三元组可以存储在一个一维数组中,从而形成一个稀疏数组。
public class Demo {
// 原始二维数组
public static int[][] createOriginalArray() {
int[][] array = new int[11][11];
array[1][2] = 1;
array[2][3] = 2;
// ... 其他非零元素赋值
return array;
}
// 将二维数组转换为稀疏数组
public static int[][] convertToSparseArray(int[][] array) {
int rows = array.length;
int cols = array[0].length;
int count = 0; // 非零元素的个数
// 计算非零元素的个数
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (array[i][j] != 0) {
count++;
}
}
}
// 创建稀疏数组
int[][] sparseArray = new int[count + 1][3];
sparseArray[0][0] = rows;
sparseArray[0][1] = cols;
sparseArray[0][2] = count;
int index = 1;
// 存储非零元素及其位置信息
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (array[i][j] != 0) {
sparseArray[index][0] = i;
sparseArray[index][1] = j;
sparseArray[index][2] = array[i][j];
index++;
}
}
}
return sparseArray;
}
// 将稀疏数组转换回二维数组
public static int[][] convertFromSparseArray(int[][] sparseArray) {
int rows = sparseArray[0][0];
int cols = sparseArray[0][1];
int[][] array = new int[rows][cols];
// 根据稀疏数组恢复二维数组
for (int i = 1; i <= sparseArray[0][2]; i++) {
int row = sparseArray[i][0];
int col = sparseArray[i][1];
int value = sparseArray[i][2];
array[row][col] = value;
}
return array;
}
public static void main(String[] args) {
int[][] originalArray = createOriginalArray();
int[][] sparseArray = convertToSparseArray(originalArray);
// 打印稀疏数组
for (int[] row : sparseArray) {
for (int val : row) {
System.out.print(val + " ");
}
System.out.println();
}
int[][] recoveredArray = convertFromSparseArray(sparseArray);
// 打印恢复后的二维数组(为了简洁,这里只打印部分数组)
for (int i = 0; i < 3; i++) { // 假设我们只关心前几行
for (int j = 0; j < 3; j++) {
System.out.print(recoveredArray[i][j] + " ");
}
System.out.println();
}
}
}
在本例中,createOriginalArray
方法用于创建一个包含一些非零元素的二维数组,convertToSparseArray
方法将该二维数组转换为稀疏数组,而 convertFromSparseArray
方法则将稀疏数组转换回原始的二维数组(或至少是它的一个等价表示)。注意,在实际应用中,我们可能需要根据具体需求调整这些方法的实现。