1. 概述

对于任何 Java 开发者来说,在处理数组操作时,写出干净、高效的代码并不总是那么容易。但数组在 Java 生态中却是核心组件之一,我们不可避免地会频繁与它们打交道。

因此,拥有一份“速查手册”——总结最常见的数组操作,对我们快速解决相关问题大有帮助。本文就是为这种场景准备的。

2. 数组与辅助类

在继续之前,我们有必要了解 Java 中的数组是什么,以及如何使用它。如果你是初次接触 Java 数组,建议先阅读 这篇基础指南,其中涵盖了所有基本概念。

需要注意的是,数组本身支持的基本操作是有限的。在数组操作中,我们常常看到为了完成简单任务而不得不使用复杂算法的情况。

因此,大多数操作我们都会借助 Java 提供的辅助类和方法:Arrays 类和 Apache 的 ArrayUtils 类。

如果要在项目中使用后者,我们需要添加 Apache Commons 依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

你可以随时在 Maven Central 上查看该依赖的最新版本。

3. 获取数组的第一个和最后一个元素

这是最常见、也最简单的任务之一,得益于数组支持按索引访问的特性。

我们先声明并初始化一个 int 数组,用于后续示例(除非特别说明):

int[] array = new int[] { 3, 5, 2, 5, 14, 4 };

由于数组的第一个元素索引为 0,并且数组有 length 属性,因此获取首尾元素非常简单:

int firstItem = array[0];
int lastItem = array[array.length - 1];

4. 获取数组中的随机元素

借助 java.util.Random 对象,我们可以轻松获取数组中的任意元素:

int anyValue = array[new Random().nextInt(array.length)];

5. 向数组追加新元素

我们知道,数组的大小是固定的,无法直接添加新元素。

所以我们要先创建一个更大的新数组,再将原数组的元素复制过去。

幸运的是,Java 的 Arrays 类提供了一个便捷方法来完成这个任务:

int[] newArray = Arrays.copyOf(array, array.length + 1);
newArray[newArray.length - 1] = newItem;

⚠️ 如果项目中引入了 ArrayUtils,我们可以使用其 add 方法(或 addAll)来一行搞定:

int[] newArray = ArrayUtils.add(array, newItem);

注意:该方法不会修改原数组,而是返回一个新数组,需要赋值给新变量。

6. 在两个元素之间插入值

由于数组是基于索引的数据结构,插入元素并不简单。

✅ Apache 提供了 ArrayUtils.insert 方法来简化操作:

int[] largerArray = ArrayUtils.insert(2, array, 77);

📌 需要指定插入位置的索引,返回的是一个新数组。

最后一个参数是可变参数(vararg),所以可以一次插入多个元素。

7. 比较两个数组

虽然数组是 Object,并提供了 equals 方法,但它使用的是默认实现,只比较引用。

✅ 我们可以调用 java.util.Arrays.equals 方法来比较两个数组的内容是否相同:

boolean areEqual = Arrays.equals(array1, array2);

⚠️ 注意:此方法不适用于 锯齿数组(jagged arrays)。对于多维数组,应使用 Arrays.deepEquals 方法。

8. 判断数组是否为空

利用数组的 length 属性,判断是否为空非常简单:

boolean isEmpty = array == null || array.length == 0;

✅ 如果引入了 ArrayUtils,还可以使用其 null-safe 方法:

boolean isEmpty = ArrayUtils.isEmpty(array);

⚠️ 但要注意,该方法仍然依赖 length 属性,对于以下情况需额外注意:

// 以下数组被认为是空的
Integer[] array1 = {};
Integer[] array2 = null;
Integer[] array3 = new Integer[0];

// 以下数组不会被认为是空的
Integer[] array4 = { null, null, null };
Integer[][] array5 = { {}, {}, {} };
Integer[] array6 = new Integer[3];

9. 打乱数组元素顺序

✅ 使用 ArrayUtils.shuffle 方法可以轻松打乱数组:

ArrayUtils.shuffle(array);

📌 这是一个 void 方法,会直接修改原数组。

10. 装箱与拆箱数组

我们常常会遇到只接受 Object 类型数组的方法。

ArrayUtils 提供了便捷方法用于装箱:

Integer[] list = ArrayUtils.toObject(array);

同样支持反向操作:

Integer[] objectArray = { 3, 5, 2, 5, 14, 4 };
int[] array = ArrayUtils.toPrimitive(objectArray);

11. 去除数组中的重复元素

✅ 最简单的方法是将数组转换为 Set

// 装箱
Integer[] list = ArrayUtils.toObject(array);
// 去重
Set<Integer> set = new HashSet<>(Arrays.asList(list));
// 创建数组并拆箱
return ArrayUtils.toPrimitive(set.toArray(new Integer[set.size()]));

📌 如果需要保持顺序,建议使用 LinkedHashSet

12. 打印数组内容

数组的 toString() 方法使用的是 Object 默认实现,输出并不直观。

ArraysArrayUtils 都提供了专用方法:

String arrayAsString = ArrayUtils.toString(array);

📌 Apache 的 toString 方法支持所有类型数组,而 Java 的 Arrays.toString() 不支持原始类型多维数组。

13. 数组映射为另一种类型

✅ 通过泛型,我们可以编写一个通用的映射方法:

public static <T, U> U[] mapObjectArray(
  T[] array, Function<T, U> function,
  Class<U> targetClazz) {
    U[] newArray = (U[]) Array.newInstance(targetClazz, array.length);
    for (int i = 0; i < array.length; i++) {
        newArray[i] = function.apply(array[i]);
    }
    return newArray;
}

📌 如果没有 Java 8,可以去掉 Function,为每种映射写专门的方法。

也可以使用 Java 8 的 Stream 实现:

String[] stringArray = Arrays.stream(array)
  .mapToObj(value -> String.format("Value: %s", value))
  .toArray(String[]::new);

14. 过滤数组中的元素

✅ 使用 Stream 可以方便地过滤数组元素:

int[] evenArray = Arrays.stream(array)
  .filter(value -> value % 2 == 0)
  .toArray();

📌 因为数组大小固定,所以过滤操作更适合使用 Stream。

15. 其他常见数组操作

除了上述内容,还有很多常见操作我们也在其他文章中详细讲解过:

16. 总结

数组是 Java 的核心功能之一,理解其工作原理和操作方式至关重要。

本文总结了在常见场景下如何高效处理数组操作。

完整代码示例可在 GitHub 仓库 中找到。


原始标题:Array Operations in Java

« 上一篇: Java Weekly, 第254期