概述
我们都知道,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 上可供查看。