HOWTO · Java
在 Java 中创建子数组
本教程将演示如何在 Java 中从另一个数组创建子数组
本页内容
子数组是复制到独立数组中的连续范围。Java 数组使用从零开始的索引:起始索引包含在范围内,结束索引表示最后一个被复制元素之后的位置。以下示例从源数组中复制索引 1 到 3 的元素。
子数组在 Java 中的含义
复制一个范围会创建新数组,而不是创建指向原数组的视图。因此,修改复制所得 int[] 中的元素不会影响源数组。对于对象数组,新数组包含相同的对象引用,因此通过任一数组修改对象本身,都可能在另一数组中看到变化。
通常使用 [start, end) 表示范围:包含 start,不包含 end。如果源数组有九个元素,索引范围是 0 到 8。从 2 到 5 的范围包含三个元素。将所需元素数量换算为结束索引时,请记住这一约定:end 等于 start + length。
使用 Arrays.copyOfRange 创建子数组
如果需要分配一个新数组,Arrays.copyOfRange 是简洁的选择。依次传入源数组、包含在范围内的起始索引和不包含在范围内的结束索引。运行以下示例。代码及显示的标签或输出保留为英文。
本节中的代码以及显示的标签或输出仍为英文。
import java.util.Arrays;
public class SubarrayExample {
public static void main(String[] args) {
int[] source = {10, 20, 30, 40, 50, 60};
int[] part = Arrays.copyOfRange(source, 1, 4);
System.out.println(Arrays.toString(part));
}
}
[20, 30, 40]
结果是一个独立数组,包含源数组索引 1、2 和 3 对应的元素。结束索引对应的位置不包含在复制范围内。允许结束索引超出源数组长度,此时结果会用组件类型的默认值填充(int 为 0,引用类型为 null)。起始索引必须位于源数组范围内或恰好等于其长度,结束索引不能小于起始索引。如果希望结束索引超出源数组时视为错误,请在调用此方法前进行验证。
使用 System.arraycopy 创建子数组
如果需要自行选择目标数组或插入位置,可以使用 System.arraycopy。它的五个参数依次是源数组、源位置、目标数组、目标位置和元素数量。最后一个参数表示数量,而不是结束索引;从目标位置开始,目标数组必须有足够空间容纳这些元素。代码及显示的标签或输出保留为英文。
本节中的代码以及显示的标签或输出仍为英文。
import java.util.Arrays;
public class SubarrayWithArraycopy {
public static void main(String[] args) {
int[] source = {10, 20, 30, 40, 50, 60};
int start = 1;
int length = 3;
int[] part = new int[length];
System.arraycopy(source, start, part, 0, length);
System.out.println(Arrays.toString(part));
}
}
这里复制范围的结束位置是 start + length,即 4;该位置不包含在范围内。请根据计划复制的元素数量分配目标数组。Java 会检查位置和长度;如果引用数组的元素类型不兼容,也可能发生数组存储错误。
使用循环创建子数组
循环可以明确展示目标数组中的偏移量,也便于在复制时检查或转换每个元素。对于 [start, end) 范围,目标索引 i - start 从零开始,并随源索引递增。代码及显示的标签或输出保留为英文。
本节中的代码以及显示的标签或输出仍为英文。
import java.util.Arrays;
public class SubarrayWithLoop {
public static void main(String[] args) {
int[] source = {10, 20, 30, 40, 50, 60};
int start = 1;
int end = 4;
int[] part = new int[end - start];
for (int i = start; i < end; i++) {
part[i - start] = source[i];
}
System.out.println(Arrays.toString(part));
}
}
循环假设这些索引在源数组中表示一个有效范围。如果索引来自用户输入,请在分配数组之前检查它们。与 Arrays.copyOfRange 不同,当请求的结束位置超出源数组时,循环不会自动填充;访问超出范围的索引会失败。如果复制时还需要应用条件、转换或其他自定义规则,循环会很有用。
使用 Stream API 创建子数组
对于 int[],IntStream.range(start, end) 会生成从起始位置(包含)到结束位置(不包含)的索引。将每个索引映射为源数组中的值,然后使用 toArray() 将这些值收集到新的基本类型数组中。代码及显示的标签或输出保留为英文。
本节中的代码以及显示的标签或输出仍为英文。
import java.util.Arrays;
import java.util.stream.IntStream;
public class SubarrayWithStream {
public static void main(String[] args) {
int[] source = {10, 20, 30, 40, 50, 60};
int[] part = IntStream.range(1, 4)
.map(i -> source[i])
.toArray();
System.out.println(Arrays.toString(part));
}
}
这种方式与前面的示例采用相同的范围约定。流生成的索引仍必须是源数组中的有效索引。如果你希望将选择过程表达为基于索引的处理流程,可以使用这种方式;流本身并不会免除范围验证。
根据使用场景选择方法
如果要直接分配数组并复制一个范围,尤其是需要利用结束位置超出源数组时的填充行为,可以选择 Arrays.copyOfRange。如果已有目标数组,或需要将复制的值放在目标数组的非零位置,可以选择 System.arraycopy。如果复制时需要对每个元素应用自定义逻辑,可以使用循环。如果组合基于索引的转换能让代码更易理解,可以选择 Stream API。
Arrays.copyOfRange、循环和 Stream API 示例都会分配新数组。System.arraycopy 会复制到你提供的目标数组中;只有在为此分配目标数组时,才会创建新的数组容器。对于基本类型数组,复制的是值;对于对象数组,复制的是引用,而不是被引用的对象本身。请根据所需的范围行为、目标数组控制方式和额外处理来选择方法;这些示例没有进行性能比较。