Java数组详解

Java Arrays - Complete Guide

掌握数组的定义、初始化、遍历与常用操作

我要打十个!

—— 叶问

数组:特殊的数据结构

假设需要记录100个数字,定义100个变量显然不切实际。此时,我们可以使用数组来存放一组相同类型的数据。

Array Diagram

数组是相同类型数据的有序集合,可以存放任何相同类型的一组内容,包括基本类型和引用类型。其中的每个数据称为数组的一个元素。

一维数组的定义与创建

数组本身也是一个对象,即使是基本类型的数组,也以对象形式存在。因此,创建数组需要使用 new 关键字。

public static void main(String[] args) {
    // 推荐的定义方式
    int[] array = new int[10]; // 创建一个长度为10的int数组

    // 其他定义方式
    int array2[] = new int[10]; // C语言风格,不推荐
    int[] array3 = new int[]{1, 2, 3}; // 静态初始化
    int[] array4 = {4, 5, 6}; // 静态初始化(仅限定义时)
}

关键点

  • 创建数组时必须指定长度。
  • 数组长度一旦确定,不可更改。
  • 数组元素有默认值(数值为0,布尔为false,引用为null)。

访问和操作数组元素

通过下标(从0开始)可以访问和修改数组中的元素。数组的长度可以通过 .length 属性获取。

public static void main(String[] args) {
    int[] array = new int[10];
    
    // 访问并打印第一个元素
    System.out.println("数组的第一个元素为:" + array[0]); // 输出: 0
    
    // 修改第一个元素的值
    array[0] = 888;
    System.out.println("修改后,第一个元素为:" + array[0]); // 输出: 888
    
    // 获取数组长度
    System.out.println("当前数组长度为:" + array.length); // 输出: 10
}

注意:数组下标访问不能越界(小于0或大于等于数组长度),否则会抛出 ArrayIndexOutOfBoundsException 异常。

遍历数组

遍历数组是常见的操作,可以使用传统的for循环或增强的for-each循环。

传统for循环

public static void main(String[] args) {
    int[] array = {1, 2, 3, 4, 5};
    for (int i = 0; i < array.length; i++) {
        System.out.print(array[i] + " ");
    }
}

增强for-each循环

for-each循环语法更简洁,适用于只需读取数组元素而无需修改的场景。

public static void main(String[] args) {
    int[] array = {1, 2, 3, 4, 5};
    for (int element : array) { // 依次取出数组中的每个元素
        System.out.print(element + " ");
    }
}

for-each循环是编译器的语法糖,其底层实现仍然是传统的for循环。

多维数组

多维数组是数组的数组。例如,二维数组可以看作一个矩阵。

public static void main(String[] args) {
    // 创建一个3行2列的二维数组
    int[][] matrix = {
        {1, 2},
        {3, 4},
        {5, 6}
    };

    // 访问第三行第二列的元素
    System.out.println(matrix[2][1]); // 输出: 6

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

可以创建更高维度的数组,但最常用的是二维数组。

可变长参数 (Varargs)

可变长参数允许方法接受任意数量的同类型参数。它本质上是一个语法糖,底层实现为数组。

public class Calculator {
    // 使用...定义可变长参数
    public int sum(int... numbers) {
        int total = 0;
        for (int num : numbers) {
            total += num;
        }
        return total;
    }
}

// 调用
Calculator calc = new Calculator();
calc.sum(1, 2);          // 传入2个参数
calc.sum(1, 2, 3, 4, 5); // 传入5个参数
calc.sum();              // 不传入参数

使用规则

  • 一个方法最多只能有一个可变长参数。
  • 可变长参数必须是方法参数列表中的最后一个。

java.util.Arrays 概览

Arrays 提供了操作数组的常用静态方法:打印、排序、搜索、比较、拷贝、填充、与流/集合互转等。

import java.util.Arrays;

public class Demo {
    public static void main(String[] args) {
        String[] names = {"Alice", "Bob"};
        int[] ints = {1, 2, 3};
        int[][] matrix = {{1, 2}, {3, 4}};

        System.out.println(Arrays.toString(names));    // [Alice, Bob]
        System.out.println(Arrays.toString(ints));     // [1, 2, 3]
        System.out.println(Arrays.deepToString(matrix)); // [[1, 2], [3, 4]]
    }
}

要点

  • toString 适用于一维数组;多维数组使用 deepToString。
  • 所有方法为静态:直接 Arrays.method(...) 调用。

排序与搜索

对基本类型或对象数组进行排序与二分搜索。

import java.util.Arrays;

public class SortSearchDemo {
    public static void main(String[] args) {
        int[] data = {5, 1, 4, 2, 3};
        Arrays.sort(data); // 升序排序
        System.out.println(Arrays.toString(data)); // [1, 2, 3, 4, 5]

        int idx = Arrays.binarySearch(data, 4); // 二分查找(数组需已排序)
        System.out.println(idx); // 结果为元素下标,未找到返回负值
    }
}

对象数组排序

  • 对象需实现 Comparable 或提供 Comparator。
  • 大数组可用 Arrays.parallelSort 提升性能。

拷贝与填充

import java.util.Arrays;

public class CopyFillDemo {
    public static void main(String[] args) {
        int[] a = {1, 2, 3};
        int[] b = Arrays.copyOf(a, 5);      // [1, 2, 3, 0, 0]
        int[] c = Arrays.copyOfRange(a, 1, 3); // [2, 3]

        Arrays.fill(b, 7); // 全部填充为 7
        System.out.println(Arrays.toString(b)); // [7, 7, 7, 7, 7]

        // 更灵活的局部拷贝
        int[] d = new int[5];
        System.arraycopy(a, 0, d, 1, 2); // 源a从0开始拷贝2个到d从1开始
        System.out.println(Arrays.toString(d)); // [0, 1, 2, 0, 0]
    }
}

注意

  • copyOf 与 copyOfRange 会创建新数组。
  • 对象数组拷贝为浅拷贝:仅复制引用。

比较与相等

import java.util.Arrays;

public class EqualsDemo {
    public static void main(String[] args) {
        int[] x = {1, 2};
        int[] y = {1, 2};
        System.out.println(Arrays.equals(x, y)); // true

        int[][] m1 = {{1, 2}, {3, 4}};
        int[][] m2 = {{1, 2}, {3, 4}};
        System.out.println(Arrays.deepEquals(m1, m2)); // true(多维数组)
    }
}

陷阱

  • == 比较的是引用地址;内容比较请用 Arrays.equals/deepEquals。

(*)与流 / 集合互转

import java.util.*;
import java.util.Arrays;

public class StreamCollectionDemo {
    public static void main(String[] args) {
        // 数值流
        int[] nums = {1, 2, 3};
        int sum = Arrays.stream(nums).sum();
        double avg = Arrays.stream(nums).average().orElse(0);
        System.out.println(sum + ", " + avg);

        // 数组转为列表(固定大小)
        Integer[] arr = {1, 2, 3};
        List fixed = Arrays.asList(arr); // 不支持 add/remove
        List list = new ArrayList<>(fixed); // 可变列表

        // 原始类型的陷阱
        int[] primitive = {1, 2, 3};
        List wrong = Arrays.asList(primitive); // size=1,元素是整个int[]
        System.out.println(wrong.size()); // 1
    }
}

提示

  • Arrays.asList 返回的列表大小固定,增删会抛异常。
  • 如需增删,请用 new ArrayList<>(Arrays.asList(...))。
  • 对原始类型数组使用 Arrays.asList 会得到单元素列表。
1 / X