Java数组详解:从基础到实战

18 阅读8分钟

当你需要存储多个同类型的数据时,比如全班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;
        }
    }
}

七、总结要点

关键知识点:

  1. 数组是固定长度的同类型数据集合,长度一旦确定就不能改变
  2. 索引从0开始,最大索引是长度-1,访问越界会抛出异常
  3. 数组是引用类型,传递的是引用而非拷贝
  4. 多维数组本质是数组的数组,每行长度可以不同

核心操作技巧:

  • 创建int[] arr = new int[5]; 或 int[] arr = {1,2,3};
  • 遍历:普通for循环或增强for循环
  • 复制Arrays.copyOf() 或 System.arraycopy()
  • 工具类:充分利用Arrays类的各种静态方法