当你需要存储多个同类型的数据时,比如全班50个学生的成绩,你会创建50个变量吗?当然不会!Java提供了数组这个强大工具,让我们可以优雅地处理大量同类型数据。今天,让我们一起深入理解Java数组的奥秘。
引入:为什么需要数组?
想象一下,你是一个班主任,需要记录全班30个学生的数学成绩。如果没有数组,你可能需要:
int score1 = 85;
int score2 = 92;
int score3 = 78;
// ... 要写30个变量!
而使用数组,你只需要:
int[] scores = {85, 92, 78, 95, 88, 76, 90, 82, 79, 85, 91, 77, 84, 89, 93, 80, 75, 86, 94, 81, 87, 92, 78, 83, 90, 85, 79, 88, 91, 76};
数组让我们可以用一个"容器"装下所有同类型的数据,并通过索引快速访问每个元素。
一、数组基础概念
什么是数组?
数组是存储在连续内存空间中的相同类型数据的集合。
二、数组的声明与创建
1. 三种创建数组的方式
public class ArrayCreation {
public static void main(String[] args) {
System.out.println("=== 三种创建数组的方式 ===\n");
// 方式1:声明并直接赋值(知道具体值时使用)
int[] scores = {85, 92, 78, 95, 88}; // 创建包含5个元素的数组
System.out.println("方式1创建的成绩数组:" + java.util.Arrays.toString(scores));
// 方式2:先声明长度,后赋值(知道长度但不知道具体值时使用)
int[] numbers = new int[3]; // 创建长度为3的整型数组,默认值都是0
numbers[0] = 10; // 给第一个元素赋值
numbers[1] = 20; // 给第二个元素赋值
numbers[2] = 30; // 给第三个元素赋值
System.out.println("方式2创建的数字数组:" + java.util.Arrays.toString(numbers));
// 方式3:先声明,后创建(分步进行)
double[] prices; // 声明一个double类型的数组变量
prices = new double[4]; // 创建长度为4的数组
prices[0] = 12.5;
prices[1] = 23.8;
prices[2] = 45.0;
prices[3] = 9.99;
System.out.println("方式3创建的价格数组:" + java.util.Arrays.toString(prices));
System.out.println("\n=== 不同数据类型的默认值 ===");
// 不同数据类型的数组有不同的默认值
boolean[] boolArr = new boolean[2]; // 默认值:false
char[] charArr = new char[2]; // 默认值:'\u0000'(空字符)
double[] doubleArr = new double[2]; // 默认值:0.0
String[] strArr = new String[2]; // 默认值:null
System.out.println("布尔数组默认值:" + boolArr[0]);
System.out.println("字符数组默认值:" + (int)charArr[0]); // 转换为数字显示
System.out.println("双精度数组默认值:" + doubleArr[0]);
System.out.println("字符串数组默认值:" + strArr[0]);
}
}
2. 数组的长度和索引
public class ArrayLengthAndIndex {
public static void main(String[] args) {
System.out.println("=== 数组长度和索引 ===\n");
String[] fruits = {"苹果", "香蕉", "橙子", "葡萄", "西瓜"};
// 获取数组长度
int length = fruits.length;
System.out.println("水果数组的长度是:" + length);
System.out.println("\n=== 访问数组元素 ===");
// 通过索引访问数组元素(索引从0开始!)
System.out.println("第一个水果:" + fruits[0]); // 索引0
System.out.println("第二个水果:" + fruits[1]); // 索引1
System.out.println("最后一个水果:" + fruits[length - 1]); // 索引4
// 修改数组元素
System.out.println("\n=== 修改数组元素 ===");
System.out.println("修改前第三个水果:" + fruits[2]);
fruits[2] = "芒果"; // 修改第三个元素
System.out.println("修改后第三个水果:" + fruits[2]);
System.out.println("\n=== 常见错误示例 ===");
// ❌ 错误1:索引越界
// System.out.println(fruits[5]); // 报错:ArrayIndexOutOfBoundsException
// fruits[5] = "菠萝"; // 报错:索引5不存在,最大索引是4
// ❌ 错误2:负数索引
// System.out.println(fruits[-1]); // 报错:索引不能为负数
System.out.println("安全访问示例:");
// ✅ 正确:先检查索引是否有效
int index = 3;
if(index >= 0 && index < fruits.length) {
System.out.println("索引" + index + "对应的水果:" + fruits[index]);
} else {
System.out.println("索引" + index + "越界!");
}
// 数组遍历的两种方法
System.out.println("\n=== 遍历数组方法1:普通for循环 ===");
for(int i = 0; i < fruits.length; i++) {
System.out.println("水果" + i + ":" + fruits[i]);
}
System.out.println("\n=== 遍历数组方法2:增强for循环 ===");
for(String fruit : fruits) {
System.out.println("水果:" + fruit);
}
}
}
三、数组的常见操作
1. 数组遍历与操作
public class ArrayOperations {
public static void main(String[] args) {
System.out.println("=== 数组常见操作 ===\n");
// 示例数组:学生成绩
int[] scores = {85, 92, 78, 95, 88, 76, 90};
// 1. 计算总分和平均分
int total = 0;
for(int score : scores) {
total += score;
}
double average = (double)total / scores.length;
System.out.println("总分:" + total);
System.out.println("平均分:" + String.format("%.2f", average));
// 2. 查找最高分和最低分
int maxScore = scores[0];
int minScore = scores[0];
for(int i = 1; i < scores.length; i++) {
if(scores[i] > maxScore) {
maxScore = scores[i];
}
if(scores[i] < minScore) {
minScore = scores[i];
}
}
System.out.println("最高分:" + maxScore);
System.out.println("最低分:" + minScore);
// 3. 查找特定元素
int target = 95;
int index = -1; // -1表示没找到
for(int i = 0; i < scores.length; i++) {
if(scores[i] == target) {
index = i;
break; // 找到后立即退出循环
}
}
if(index != -1) {
System.out.println("找到分数" + target + ",位置在索引" + index);
} else {
System.out.println("没有找到分数" + target);
}
// 4. 统计分数段人数
int excellent = 0; // 优秀:90分以上
int good = 0; // 良好:80-89分
int pass = 0; // 及格:60-79分
int fail = 0; // 不及格:60分以下
for(int score : scores) {
if(score >= 90) {
excellent++;
} else if(score >= 80) {
good++;
} else if(score >= 60) {
pass++;
} else {
fail++;
}
}
System.out.println("\n分数段统计:");
System.out.println("优秀(≥90):" + excellent + "人");
System.out.println("良好(80-89):" + good + "人");
System.out.println("及格(60-79):" + pass + "人");
System.out.println("不及格(<60):" + fail + "人");
}
}
2. 多维数组
public class MultiDimensionalArray {
public static void main(String[] args) {
System.out.println("=== 多维数组:二维数组 ===\n");
// 二维数组:可以想象成一个表格或矩阵
// 方式1:直接初始化
int[][] matrix1 = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 方式2:先声明大小,后赋值
int rows = 3;
int cols = 4;
int[][] matrix2 = new int[rows][cols];
// 填充二维数组
int value = 1;
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
matrix2[i][j] = value++;
}
}
System.out.println("二维数组1(3x3):");
printMatrix(matrix1);
System.out.println("\n二维数组2(3x4):");
printMatrix(matrix2);
// 三维数组示例
int[][][] cube = new int[2][3][4];
System.out.println("\n三维数组维度:" + cube.length + "x" + cube[0].length + "x" + cube[0][0].length);
}
// 打印二维数组的方法
public static void printMatrix(int[][] matrix) {
for(int i = 0; i < matrix.length; i++) {
for(int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + "\t");
}
System.out.println();
}
}
}
四、数组工具类:Arrays
import java.util.Arrays;
public class ArraysClassDemo {
public static void main(String[] args) {
System.out.println("=== Arrays工具类常用方法 ===\n");
int[] numbers = {5, 2, 8, 1, 9, 3};
// 1. 数组排序
System.out.println("排序前:" + Arrays.toString(numbers));
Arrays.sort(numbers); // 原地排序
System.out.println("排序后:" + Arrays.toString(numbers));
// 2. 二分查找(必须先排序)
int index = Arrays.binarySearch(numbers, 8);
System.out.println("查找数字8的位置:" + index);
// 3. 数组填充
int[] filledArray = new int[5];
Arrays.fill(filledArray, 10); // 所有元素填充为10
System.out.println("填充数组:" + Arrays.toString(filledArray));
// 4. 数组复制
int[] original = {1, 2, 3, 4, 5};
int[] copy1 = Arrays.copyOf(original, 3); // 复制前3个
int[] copy2 = Arrays.copyOfRange(original, 1, 4); // 复制索引1到3
System.out.println("原数组:" + Arrays.toString(original));
System.out.println("复制前3个:" + Arrays.toString(copy1));
System.out.println("复制索引1-3:" + Arrays.toString(copy2));
// 5. 数组比较
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
int[] arr3 = {1, 2, 4};
System.out.println("arr1等于arr2吗?" + Arrays.equals(arr1, arr2));
System.out.println("arr1等于arr3吗?" + Arrays.equals(arr1, arr3));
// 6. 深度比较(用于多维数组)
int[][] deepArr1 = {{1, 2}, {3, 4}};
int[][] deepArr2 = {{1, 2}, {3, 4}};
System.out.println("多维数组深度比较:" + Arrays.deepEquals(deepArr1, deepArr2));
}
}
五、数组常见问题与解决方案
public class ArrayCommonProblems {
public static void main(String[] args) {
System.out.println("=== 数组常见问题与解决方案 ===\n");
// 问题1:空指针异常
System.out.println("问题1:空指针异常");
int[] arr = null;
// System.out.println(arr.length); // 会抛出NullPointerException
// 解决方案:先检查是否为null
if(arr != null) {
System.out.println("数组长度:" + arr.length);
} else {
System.out.println("数组为空");
}
// 问题2:数组越界
System.out.println("\n问题2:数组越界");
int[] scores = {85, 92, 78};
// System.out.println(scores[5]); // 会抛出ArrayIndexOutOfBoundsException
// 解决方案:检查索引范围
int index = 2;
if(index >= 0 && index < scores.length) {
System.out.println("索引" + index + "的值:" + scores[index]);
}
// 问题3:数组作为参数传递
System.out.println("\n问题3:数组作为参数传递");
int[] nums = {1, 2, 3};
System.out.println("修改前:" + Arrays.toString(nums));
modifyArray(nums);
System.out.println("修改后:" + Arrays.toString(nums));
// 问题4:数组拷贝的陷阱
System.out.println("\n问题4:数组拷贝的陷阱");
int[] source = {1, 2, 3};
int[] shallowCopy = source; // 浅拷贝:两个变量指向同一个数组
shallowCopy[0] = 100;
System.out.println("浅拷贝后,原数组也被修改:" + Arrays.toString(source));
// 解决方案:深拷贝
int[] deepCopy = Arrays.copyOf(source, source.length);
deepCopy[0] = 999;
System.out.println("深拷贝后,原数组不变:" + Arrays.toString(source));
}
// 数组作为参数传递是传递引用
public static void modifyArray(int[] arr) {
if(arr != null && arr.length > 0) {
arr[0] = 100; // 修改会影响原数组
}
}
}
六、数组性能与最佳实践
public class ArrayPerformance {
public static void main(String[] args) {
System.out.println("=== 数组性能与最佳实践 ===\n");
// 1. 数组与ArrayList的选择
System.out.println("选择数组的场景:");
System.out.println("• 数据长度固定且已知");
System.out.println("• 对性能要求极高");
System.out.println("• 存储基本数据类型");
System.out.println("• 多维数据结构");
System.out.println("\n选择ArrayList的场景:");
System.out.println("• 数据长度经常变化");
System.out.println("• 需要方便的添加/删除操作");
System.out.println("• 需要丰富的内置方法");
// 2. 性能对比示例
int size = 1000000;
System.out.println("\n性能测试(" + size + "个元素):");
long start = System.currentTimeMillis();
int[] array = new int[size];
for(int i = 0; i < size; i++) {
array[i] = i;
}
long arrayTime = System.currentTimeMillis() - start;
start = System.currentTimeMillis();
java.util.ArrayList<Integer> list = new java.util.ArrayList<>();
for(int i = 0; i < size; i++) {
list.add(i);
}
long listTime = System.currentTimeMillis() - start;
System.out.println("数组耗时:" + arrayTime + "ms");
System.out.println("ArrayList耗时:" + listTime + "ms");
System.out.println("数组比ArrayList快约 " + String.format("%.1f", (double)listTime/arrayTime) + " 倍");
// 3. 最佳实践
System.out.println("\n=== 数组最佳实践 ===");
System.out.println("1. 尽量预估数组大小,避免频繁扩容");
System.out.println("2. 对于基本类型,优先使用数组而非包装类集合");
System.out.println("3. 大量数据操作时,考虑使用System.arraycopy()");
System.out.println("4. 多维数组要考虑内存连续性对性能的影响");
System.out.println("5. 敏感数据使用后要及时清空(Arrays.fill(arr, 0))");
// 4. 高级技巧:数组模拟简单数据结构
System.out.println("\n=== 使用数组模拟栈 ===");
ArrayStack stack = new ArrayStack(5);
stack.push(10);
stack.push(20);
stack.push(30);
System.out.println("栈顶元素:" + stack.peek());
System.out.println("弹出元素:" + stack.pop());
System.out.println("栈大小:" + stack.size());
}
// 使用数组实现简单栈
static class ArrayStack {
private int[] data;
private int top;
public ArrayStack(int capacity) {
data = new int[capacity];
top = -1;
}
public void push(int value) {
if(top < data.length - 1) {
data[++top] = value;
}
}
public int pop() {
if(top >= 0) {
return data[top--];
}
return -1;
}
public int peek() {
if(top >= 0) {
return data[top];
}
return -1;
}
public int size() {
return top + 1;
}
}
}
七、总结要点
关键知识点:
- 数组是固定长度的同类型数据集合,长度一旦确定就不能改变
- 索引从0开始,最大索引是
长度-1,访问越界会抛出异常 - 数组是引用类型,传递的是引用而非拷贝
- 多维数组本质是数组的数组,每行长度可以不同
核心操作技巧:
- 创建:
int[] arr = new int[5];或int[] arr = {1,2,3}; - 遍历:普通for循环或增强for循环
- 复制:
Arrays.copyOf()或System.arraycopy() - 工具类:充分利用
Arrays类的各种静态方法