概述

我们都知道,Java 的 List 类提供了 subList() 方法,可以让我们截取源 List 对象的一部分。然而,在数组方面,标准库并没有提供 subArray() 方法。

本教程将探讨如何在 Java 中从给定数组获取子数组。

问题介绍

如往常一样,我们通过一个例子来理解问题。假设我们有一个字符串数组:

String[] LANGUAGES = new String[] { "Python", "Java", "Kotlin", "Scala", "Ruby", "Go", "Rust" };

可以看到,LANGUAGES 数组包含一些编程语言名称。由于应用程序通常用 "Java"、"Kotlin" 或 "Scala" 编写,并且这些语言能在 Java 虚拟机上运行,我们想获取包含这三个元素的子数组。换句话说,我们希望从 LANGUAGES 数组的第二个到第四个元素(索引 1、2、3):

String[] JVM_LANGUAGES = new String[] { "Java", "Kotlin", "Scala" };

接下来,我们将探讨不同的解决方案,并使用单元测试断言来验证每个方法是否按预期工作。

现在,让我们动手实现。

使用 Stream API

Java 8 引入的一个重要新特性是 Stream API。如果我们的 Java 版本为 8 或更高,我们可以使用 Stream API 来切片给定的数组。

首先,我们可以使用 Arrays.stream() 方法将数组转换为 Stream 对象。需要注意的是,我们应该使用带三个参数的 Arrays.stream() 方法:

  • 数组 - 在这个例子中是 LANGUAGES
  • 开始索引(包括)- 从数组中提取的起始位置
  • 结束索引(不包括)- 提取的结束位置

因此,为了解决这个问题,我们可以将 LANGUAGES、1 和 4 作为参数传递给 Arrays.stream() 方法。

接下来,我们创建一个测试来看看它是否能获取我们想要的子数组:

String[] result = Arrays.stream(LANGUAGES, 1, 4).toArray(String[]::new);
assertArrayEquals(JVM_LANGUAGES, result);

如上代码所示,将数组转换为 Stream 后,我们可以调用 toArray() 方法将其转换回数组。

如果运行测试,它会通过,说明它完成了任务。

使用 Arrays.copyOfRange() 方法

我们已经学会了使用 Stream API 解决问题。但是,Stream API 只有在 Java 8 及以后版本可用

如果我们的 Java 版本为 6 或更高,我们可以使用 Arrays.copyOfRange() 方法来解决问题。这个方法的参数类似于 Arrays.stream() 方法——数组、起始索引(包括)和结束索引(不包括)。

现在,让我们创建一个测试,看看 Arrays.copyOfRange() 是否能解决问题:

String[] result = Arrays.copyOfRange(LANGUAGES, 1, 4);
assertArrayEquals(JVM_LANGUAGES, result);

如果运行测试,它会通过,说明它也解决了问题。

使用 System.arraycopy() 方法

Arrays.copyOfRange() 方法通过复制给定数组的一部分到新数组来解决问题。

当我们需要从数组中复制一部分时,除了 Arrays.copyOfRange() 方法外,还可以使用 System.arraycopy() 方法。接下来,让我们用这个方法解决这个问题。

我们已经看到 Arrays.copyOfRange() 返回结果子数组。然而,System.arraycopy() 方法的返回类型是 void。因此,我们必须创建一个新的数组对象,并将其传递给 arraycopy() 方法。该方法会在数组中填充复制的元素:

String[] result = new String[3];
System.arraycopy(LANGUAGES, 1, result, 0, 3);
assertArrayEquals(JVM_LANGUAGES, result);

如果运行测试,它会通过。

如上代码所示,arraycopy() 方法有五个参数,含义如下:

  • 源数组 - LANGUAGE
  • 源数组中要复制的起始索引 - 1
  • 目标数组,用于存储复制结果 - result
  • 目标数组中存储复制结果的起始索引 - 0
  • 从源数组中要复制的元素数量 - 3

值得注意的是,如果结果数组中已有数据,arraycopy() 方法可能会覆盖原有数据

String[] result2 = new String[] { "value one", "value two", "value three", "value four", "value five", "value six", "value seven" };
System.arraycopy(LANGUAGES, 1, result2, 2, 3);
assertArrayEquals(new String[] { "value one", "value two", "Java", "Kotlin", "Scala", "value six", "value seven" }, result2);

在这个例子中,result2 数组包含七个元素。当我们调用 arraycopy() 方法时,告诉它从 result2 的索引 2 开始填充复制的数据。正如我们所见,复制的三个元素覆盖了原始元素,因为 result2

另外,我们需要知道 System.arraycopy() 是一个原生方法,而 Arrays.copyOfRange() 方法内部调用了 System.arraycopy()

使用 Apache Commons Lang3 库的 ArrayUtils

Apache Commons Lang3 是一个广泛使用的库,它的 ArrayUtils 类提供了许多方便的方法,使我们能够更轻松地处理数组。

最后,让我们使用 ArrayUtils 类来解决问题。

在开始使用 ArrayUtils 之前,我们需要在 Maven 配置中添加依赖:

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

当然,您可以在 Maven 中央仓库找到最新版本。

ArrayUtils 类有一个 subarray() 方法,可以快速获取子数组:

String[] result = ArrayUtils.subarray(LANGUAGES, 1, 4);
assertArrayEquals(JVM_LANGUAGES, result);

使用 subarray() 方法解决这个问题非常直观。

总结

在这篇文章中,我们学习了从给定数组获取子数组的不同方法。

如往常一样,这里展示的所有代码片段都在 GitHub 上可供查看。