在 Java 里,数组属于一种数据结构,它能够存储固定数量的相同类型元素。
数组在编程中是一种非常基础且重要的数据结构,它被广泛使用是因为能带来诸多便利
1. 数据集中管理
当你需要处理多个相同类型的数据时,数组能够把这些数据集中存储。比如,要记录一个班级 50 个学生的考试成绩,若不使用数组,就需要为每个成绩单独创建一个变量,这会使代码变得冗长且难以管理。而使用数组,仅需一个数组变量就能存储所有成绩,示例代码如下:
// 创建一个能存储 50 个成绩的数组
double[] scores = new double[50];
2. 方便数据访问
数组中的元素可以通过索引快速访问。索引是一个整数,代表元素在数组中的位置,从 0 开始计数。这种特性使得程序能够直接定位到所需的数据,极大地提高了数据访问的效率。例如,要获取上述班级中第 10 个学生的成绩,只需通过索引操作即可:
// 获取第 10 个学生的成绩
double tenthScore = scores[9];
3. 便于数据处理和算法实现
很多算法和数据处理操作都是基于数组来实现的。例如,排序算法(如冒泡排序、快速排序)、搜索算法(如线性搜索、二分搜索)等,这些算法在数组上能够高效地运行。
4. 内存连续性
在大多数编程语言中,数组在内存中是连续存储的。这意味着数组中的元素在内存中是一个接一个排列的,这种存储方式使得计算机能够更高效地访问数组元素,因为它可以利用 CPU 的缓存机制,减少内存访问的时间开销。
5. 支持多维数据表示
数组可以扩展为多维数组,用于表示更复杂的数据结构,如矩阵、表格等。例如,在图像处理中,可以使用二维数组来表示图像的像素矩阵;在游戏开发中,可以使用三维数组来表示三维空间中的物体位置
数组的使用
1. 数组的声明
数组声明时需要明确元素的类型和数组名称,格式如下:
// 声明一维数组
dataType[] arrayName;
// 或者
dataType arrayName[];
// 示例
int[] numbers;
String names[];
2. 数组的初始化
数组初始化可以采用静态初始化或者动态初始化。
静态初始化
在创建数组时就为其赋值,格式如下:
dataType[] arrayName = {value1, value2, ..., valueN};
// 示例
int[] numbers = {1, 2, 3, 4, 5};
String[] names = {"Alice", "Bob", "Charlie"};
动态初始化
先创建数组,之后再为其元素赋值,格式如下:
dataType[] arrayName = new dataType[arraySize];
// 示例
// 此时 会对 数组中的元素初始化,里边存的是int的默认值:0
int[] numbers = new int[5];
// 为元素赋值
numbers[0] = 1;
numbers[1] = 2;
3. 数组的访问
数组元素可通过索引来访问,索引从 0 开始。
int[] numbers = {1, 2, 3, 4, 5};
// 访问第一个元素
int firstNumber = numbers[0];
System.out.println(firstNumber); // 输出 1
4. 数组的遍历
length
是数组的一个属性,用于获取数组的长度。数组的 length
属性是一个final类型的常量,这意味着你无法直接修改它。一旦数组被创建,其长度就固定了,不能再改变
在for遍历数组时,length
属性十分有用,它能确保循环访问到数组的每个元素,同时避免越界访问。
使用 for
循环
int[] numbers = {1, 2, 3, 4, 5};
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]);
}
使用 for-each
循环
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
System.out.println(number);
}
5. 多维数组
多维数组就是数组的数组,常见的有二维数组。
多维数组的 length
属性
对于多维数组,length
属性返回的是数组第一维的长度。如果要获取其他维度的长度,需要对相应的子数组使用 length
属性。
二维数组的声明和初始化
// 静态初始化
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 动态初始化
int[][] matrix2 = new int[3][3];
matrix2[0][0] = 1;
matrix2[0][1] = 2;
// 依此类推
二维数组的遍历
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();
}
6. 数组的常用方法
Java 提供了一些实用的数组方法,例如 Arrays
类中的方法。
import java.util.Arrays;
public class ArrayExample {
public static void main(String[] args) {
int[] numbers = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
//(值复制)
int[] deepCopy = Arrays.copyOf(numbers, numbers.length);
// 对数组进行排序
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers));
System.out.println(Arrays.toString(deepCopy));
// 查找元素
int index = Arrays.binarySearch(numbers, 5);
System.out.println("元素 5 的索引是: " + index);
}
}
7. 数组是引用类型
在 Java 里,内存主要分为堆内存和栈内存。
栈内存
栈内存用于存储局部变量和方法调用信息。当你声明一个数组变量时,这个变量就会被存储在栈内存中,不过它仅仅是一个引用,并非实际的数组对象。
堆内存
堆内存用于存储对象实例。当你使用 new
关键字创建一个数组时,实际的数组对象会被分配到堆内存中,而栈内存中的引用变量则指向这个堆内存中的数组对象
数组作为引用类型的特性
-
引用传递案例
- 方法内修改数组元素
解析:public class Test { public static void main(String[] args) { int[] a = {1, 2, 3}; fun(a); // 传递引用 System.out.println("a[0] = " + a[0]); // 输出25 } public static void fun(int[] b) { b[0] = 25; // 修改原数组 } }
a
和b
指向同一内存地址,修改b
即修改a
- 方法内修改数组元素
-
数组赋值与引用
int[] arr1 = {1, 2, 3}; int[] arr2 = arr1; // arr2引用arr1的地址 arr2[0] = 100; // arr1[0]同步变为100
- 多态性:数组可指向子类对象(需父类引用子类实例)
Number[] nums = new Integer[3]; // 允许
-
package com.example.demo1; public class Test1 { public static Object arr[]; public static void main(String[] args) { arr = new Integer[3]; // 允许; arr[0] = 1; arr[1] = 2; arr[2] = 3; Integer tmp[] = (Integer[])(arr); for (int index = 0; index < arr.length; index++) { System.out.println(tmp[index]); } } }
常见问题与注意事项
-
空指针异常(NullPointerException)
int[] arr = null; System.out.println(arr[0]); // 抛出异常
-
数组越界(ArrayIndexOutOfBoundsException)
int[] arr = {1, 2, 3}; System.out.println(arr[3]); // 索引范围0-2
-
数组是对象,存储在堆内存中,变量名是引用,拷贝数组需使用
Arrays.copyOf()
,避免直接赋值导致引用共享 -
默认值规则
综合案例,评委打分
int len = 6;
// 定义一个包含 6 个评委打分的数组
int[] scores = new int[len];
Random r = new Random();
for (int i = 0; i < scores.length; i++) {
scores[i] = r.nextInt(10,100);
}
// 初始化最高分和最低分
int max = scores[0];
int min = scores[0];
int sum = 0;
// 遍历数组,找出最高分、最低分并求和
for (int score : scores) {
if (score > max) {
max = score;
}
if (score < min) {
min = score;
}
sum += score;
}
// 去掉最高分和最低分后的总分
int finalSum = sum - max - min;
// 计算平均分. 整数操作会去掉小数部分。所以,除之前,先变成浮点数,在操作
double average = (double)finalSum / (scores.length - 2);
// double average = finalSum * 1.0 / (scores.length - 2);
System.out.println("各位评委打分结果: " + Arrays.toString(scores));
System.out.printf("去掉最高分%d和最低分%d后的平均分是%.2f: " ,max,min,average);