(六)Java数组详解

一、数组概述

数组是Java编程中最基础且重要的数据结构之一,它是相同类型数据的有序集合。数组在内存中以连续的方式存储,这种存储结构使得数组具有高效的随机访问特性。

1.1 数组的基本概念

数组(Array)是由相同类型的元素(element)组成的数据结构,这些元素在内存中按照线性顺序排列。每个元素都可以通过数组索引(index)来访问,索引从0开始计数。

数组的主要特点包括:

  • 固定长度:数组一旦创建,其长度就不可改变

  • 类型一致:所有元素必须是相同的数据类型

  • 内存连续:元素在内存中是连续存储的

  • 高效访问:通过索引可以直接访问任意元素,时间复杂度为O(1)

1.2 数组的应用场景

数组在编程中有着广泛的应用:

  • 存储大量相同类型的数据

  • 实现矩阵、表格等数据结构

  • 作为其他复杂数据结构的基础(如堆、栈、队列等)

  • 算法实现(排序、搜索等)

二、数组的声明与初始化

2.1 数组的声明

在Java中,数组的声明有以下几种方式:

java

// 方式一:数据类型[] 数组名;
int[] arr1;

// 方式二:数据类型 数组名[];
int arr2[];

推荐使用第一种方式,因为它更清楚地表明这是一个"int数组"类型。

2.2 数组的初始化

数组初始化分为静态初始化和动态初始化两种方式。

2.2.1 静态初始化

静态初始化是指在声明数组的同时直接指定元素内容:

java

// 完整格式
int[] arr1 = new int[]{1, 2, 3, 4, 5};

// 简化格式
int[] arr2 = {1, 2, 3, 4, 5};

// 字符串数组示例
String[] names = {"Alice", "Bob", "Charlie"};
2.2.2 动态初始化

动态初始化是指只指定数组长度,由系统为数组元素分配初始值:

java

// 格式:数据类型[] 数组名 = new 数据类型[数组长度];
int[] arr = new int[5];  // 创建一个长度为5的int数组,元素初始值为0

// 其他类型的默认值:
// boolean数组默认值为false
// 引用类型数组默认值为null
// double数组默认值为0.0

2.3 数组的默认值

当数组动态初始化时,系统会为数组元素分配默认值:

  • 整数类型(byte, short, int, long):0

  • 浮点类型(float, double):0.0

  • 字符类型(char):'\u0000'(空字符)

  • 布尔类型(boolean):false

  • 引用类型:null

三、数组的基本操作

3.1 访问数组元素

数组元素通过索引访问,索引范围从0到数组长度-1:

java

int[] numbers = {10, 20, 30, 40, 50};

// 访问第一个元素
System.out.println(numbers[0]);  // 输出10

// 修改第三个元素
numbers[2] = 33;

// 遍历数组
for (int i = 0; i < numbers.length; i++) {
    System.out.println(numbers[i]);
}

3.2 数组长度

数组的length属性表示数组的长度:

java

int[] arr = new int[10];
System.out.println(arr.length);  // 输出10

注意:length是属性而不是方法,不要加括号。

3.3 数组遍历

常见的数组遍历方式有以下几种:

3.3.1 普通for循环

java

for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}
3.3.2 增强for循环(foreach)

java

for (int num : arr) {
    System.out.println(num);
}
3.3.3 使用Arrays.toString()

java

System.out.println(Arrays.toString(arr));

3.4 数组越界问题

Java会对数组访问进行边界检查,如果访问超出范围的索引,会抛出ArrayIndexOutOfBoundsException:

java

int[] arr = {1, 2, 3};
System.out.println(arr[3]);  // 运行时错误:ArrayIndexOutOfBoundsException

四、多维数组

Java支持多维数组,最常见的是二维数组。

4.1 二维数组的声明与初始化

4.1.1 静态初始化

java

// 标准形式
int[][] matrix1 = new int[][]{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// 简化形式
int[][] matrix2 = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
4.1.2 动态初始化

java

// 规则二维数组
int[][] matrix3 = new int[3][4];  // 3行4列

// 不规则二维数组
int[][] matrix4 = new int[3][];
matrix4[0] = new int[2];
matrix4[1] = new int[3];
matrix4[2] = new int[4];

4.2 二维数组的访问与遍历

java

// 访问特定元素
int value = matrix[1][2];

// 遍历二维数组
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();
}

// 使用增强for循环遍历
for (int[] row : matrix) {
    for (int num : row) {
        System.out.print(num + " ");
    }
    System.out.println();
}

4.3 多维数组的内存模型

在Java中,多维数组实际上是"数组的数组"。例如,二维数组可以看作是一个一维数组,其中每个元素又是一个一维数组。

五、数组的常用操作与工具类

5.1 数组拷贝

5.1.1 System.arraycopy()

java

 int[] src = {1, 2, 3, 4, 5};
int[] dest = new int[5];

// 参数:源数组, 源起始位置, 目标数组, 目标起始位置, 拷贝长度
System.arraycopy(src, 0, dest, 0, src.length);
5.1.2 Arrays.copyOf()

java

int[] original = {1, 2, 3, 4, 5};
int[] copy = Arrays.copyOf(original, original.length);

// 可以指定新长度,多余位置补默认值
int[] longerCopy = Arrays.copyOf(original, 10);

5.2 数组排序

java

int[] numbers = {5, 3, 9, 1, 6};

// 升序排序
Arrays.sort(numbers);

// 部分排序
Arrays.sort(numbers, 1, 4);  // 对索引1到3的元素排序

// 自定义排序(需实现Comparator接口)
Integer[] nums = {5, 3, 9, 1, 6};
Arrays.sort(nums, (a, b) -> b - a);  // 降序排序

5.3 数组搜索

java

int[] sortedArr = {1, 3, 5, 7, 9};

// 二分查找(数组必须已排序)
int index = Arrays.binarySearch(sortedArr, 5);  // 返回2

// 如果元素不存在,返回-(插入点)-1
int notFound = Arrays.binarySearch(sortedArr, 4);  // 返回-3

5.4 数组比较

java

int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
int[] arr3 = {1, 3, 2};

System.out.println(Arrays.equals(arr1, arr2));  // true
System.out.println(Arrays.equals(arr1, arr3));  // false

5.5 数组填充

java

int[] arr = new int[5];

// 填充整个数组
Arrays.fill(arr, 10);  // [10, 10, 10, 10, 10]

// 填充部分数组
Arrays.fill(arr, 1, 3, 20);  // [10, 20, 20, 10, 10]

5.6 数组转列表

java

String[] names = {"Alice", "Bob", "Charlie"};

// 返回的List是Arrays的内部类,不支持add/remove操作
List<String> nameList = Arrays.asList(names);

// 如果需要可修改的List
List<String> modifiableList = new ArrayList<>(Arrays.asList(names));

六、数组与方法的交互

6.1 数组作为方法参数

java

public static void printArray(int[] arr) {
    for (int num : arr) {
        System.out.print(num + " ");
    }
    System.out.println();
}

// 调用
int[] numbers = {1, 2, 3};
printArray(numbers);

6.2 数组作为返回值

java

public static int[] reverseArray(int[] arr) {
    int[] result = new int[arr.length];
    for (int i = 0; i < arr.length; i++) {
        result[arr.length - 1 - i] = arr[i];
    }
    return result;
}

// 调用
int[] original = {1, 2, 3, 4, 5};
int[] reversed = reverseArray(original);

6.3 可变参数

可变参数本质上就是数组,但提供了更灵活的调用方式:

java

public static int sum(int... numbers) {
    int total = 0;
    for (int num : numbers) {
        total += num;
    }
    return total;
}

// 调用方式
System.out.println(sum(1, 2, 3));      // 输出6
System.out.println(sum(1, 2, 3, 4, 5)); // 输出15
System.out.println(sum(new int[]{1, 2, 3})); // 也可以直接传入数组

七、数组的高级特性

7.1 数组与泛型

Java不允许直接创建泛型数组:

java

// 以下代码编译错误
// List<String>[] arrayOfLists = new List<String>[10];

// 变通方案:使用原始类型或通配符类型
List<?>[] arrayOfLists = new List<?>[10];

7.2 数组与集合的转换

java

// 数组转List
String[] names = {"Alice", "Bob", "Charlie"};
List<String> nameList = Arrays.asList(names);  // 固定大小List
List<String> modifiableList = new ArrayList<>(Arrays.asList(names));

// List转数组
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
String[] array1 = list.toArray(new String[0]);  // 推荐方式
String[] array2 = list.toArray(new String[list.size()]);

7.3 数组的性能考虑

数组在某些场景下比集合类更高效:

  • 内存占用更小(没有额外的对象开销)

  • 访问速度更快(直接内存访问)

  • 更适合固定大小的数据结构

但数组也有局限性:

  • 长度固定

  • 功能有限(需要手动实现排序、搜索等操作)

  • 不支持泛型

八、常见数组算法

8.1 数组反转

java

public static void reverse(int[] arr) {
    for (int i = 0; i < arr.length / 2; i++) {
        int temp = arr[i];
        arr[i] = arr[arr.length - 1 - i];
        arr[arr.length - 1 - i] = temp;
    }
}

8.2 查找最大值/最小值

java

public static int findMax(int[] arr) {
    int max = arr[0];
    for (int num : arr) {
        if (num > max) {
            max = num;
        }
    }
    return max;
}

public static int findMin(int[] arr) {
    int min = arr[0];
    for (int num : arr) {
        if (num < min) {
            min = num;
        }
    }
    return min;
}

8.3 数组求和与平均值

java

public static int sum(int[] arr) {
    int total = 0;
    for (int num : arr) {
        total += num;
    }
    return total;
}

public static double average(int[] arr) {
    return (double) sum(arr) / arr.length;
}

8.4 数组合并

java

public static int[] mergeArrays(int[] arr1, int[] arr2) {
    int[] result = new int[arr1.length + arr2.length];
    System.arraycopy(arr1, 0, result, 0, arr1.length);
    System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
    return result;
}

8.5 数组元素去重

java

public static int[] removeDuplicates(int[] arr) {
    if (arr.length == 0) return arr;
    
    Arrays.sort(arr);
    int uniqueCount = 1;
    for (int i = 1; i < arr.length; i++) {
        if (arr[i] != arr[i-1]) {
            uniqueCount++;
        }
    }
    
    int[] result = new int[uniqueCount];
    result[0] = arr[0];
    int index = 1;
    for (int i = 1; i < arr.length; i++) {
        if (arr[i] != arr[i-1]) {
            result[index++] = arr[i];
        }
    }
    return result;
}

九、数组的常见问题与解决方案

9.1 数组越界问题

问题:访问不存在的索引位置
解决方案:始终检查索引范围,确保在0到length-1之间

java

int[] arr = {1, 2, 3};
int index = 3;
if (index >= 0 && index < arr.length) {
    System.out.println(arr[index]);
} else {
    System.out.println("Invalid index");
}

9.2 空指针异常

问题:数组未初始化就使用
解决方案:在使用前检查数组是否为null

java

int[] arr = null;
if (arr != null) {
    System.out.println(arr.length);
} else {
    System.out.println("Array is null");
}

9.3 数组长度不可变

问题:数组创建后长度固定
解决方案:需要扩展数组时,创建新数组并拷贝数据

java

int[] original = {1, 2, 3};
int[] larger = Arrays.copyOf(original, original.length * 2);

9.4 多维数组不规则问题

问题:多维数组的子数组长度不一致可能导致错误
解决方案:访问多维数组元素时检查子数组长度

java

int[][] matrix = new int[3][];
matrix[0] = new int[2];
matrix[1] = new int[3];
matrix[2] = new int[4];

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();
}

十、Java 8+中的数组新特性

10.1 使用Stream处理数组

java

int[] numbers = {1, 2, 3, 4, 5};

// 求和
int sum = Arrays.stream(numbers).sum();

// 平均值
OptionalDouble avg = Arrays.stream(numbers).average();

// 过滤
int[] evens = Arrays.stream(numbers).filter(n -> n % 2 == 0).toArray();

// 映射
int[] squares = Arrays.stream(numbers).map(n -> n * n).toArray();

// 排序
int[] sorted = Arrays.stream(numbers).sorted().toArray();

10.2 并行数组处理

java

int[] largeArray = new int[1000000];
// 填充数组...

// 并行排序
Arrays.parallelSort(largeArray);

// 并行计算
int sum = Arrays.stream(largeArray).parallel().sum();

10.3 Arrays新增方法

Java 8之后,Arrays类增加了一些实用方法:

java

// setAll
int[] squares = new int[10];
Arrays.setAll(squares, i -> i * i);

// parallelPrefix (累积计算)
int[] numbers = {1, 2, 3, 4, 5};
Arrays.parallelPrefix(numbers, (a, b) -> a + b);
// 结果:[1, 3, 6, 10, 15]

十一、性能优化与最佳实践

11.1 数组与集合的选择

  • 使用数组的场景:

    • 元素数量固定且已知

    • 需要极致性能

    • 基本数据类型存储

    • 多维数据结构

  • 使用集合的场景:

    • 需要动态扩容

    • 需要丰富的API操作

    • 对象类型存储

    • 需要线程安全

11.2 数组拷贝的性能考虑

  • 对于小数组,各种拷贝方法差异不大

  • 对于大数组,System.arraycopy()性能最佳

  • Arrays.copyOf()内部使用System.arraycopy(),但更简洁

11.3 多维数组的内存布局

  • Java多维数组是"数组的数组",不保证内存完全连续

  • 对于性能关键的多维数组操作,可以考虑使用一维数组模拟:

java

// 二维数组模拟
int rows = 3, cols = 4;
int[] matrix = new int[rows * cols];

// 访问第i行第j列的元素
int value = matrix[i * cols + j];

11.4 避免常见的性能陷阱

  • 避免在循环中重复计算数组长度:

    java

    // 不好
    for (int i = 0; i < arr.length; i++) {...}
    
    // 更好(对于大数组)
    int len = arr.length;
    for (int i = 0; i < len; i++) {...}

  • 预先分配足够大的数组,避免频繁扩容

  • 考虑缓存友好性,尽量顺序访问数组元素

十二、总结

数组作为Java中最基础的数据结构,具有简单、高效的特性。通过本文的学习,我们系统地了解了:

  1. 数组的基本概念和特点

  2. 数组的声明、初始化和基本操作

  3. 多维数组的使用方法

  4. 数组与工具类的配合使用

  5. 数组与方法的交互方式

  6. 数组的高级特性和性能优化

  7. 常见数组算法和问题解决方案

  8. Java新版本中的数组增强功能

掌握数组不仅有助于理解更复杂的数据结构,也是编写高效Java程序的基础。在实际开发中,应根据具体需求合理选择数组或集合类,并注意避免数组常见的问题和性能陷阱。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值