HOWTO · Java

在 Java 中对数组进行切片

本文将帮助你用 Java 将数组切片为子数组。

本页内容

要对 Java 数组进行切片,可以把一个连续范围内的元素复制到一个新数组中。下面的示例使用左闭右开的范围:fromIndex 包含在内,toIndex 是第一个不包含在内的索引。例如,范围 [2, 5) 选取的是索引 2、3 和 4。原始数组保持不变。

在 Java 中通过复制元素对数组进行切片

用循环可以让索引的对应关系一目了然。先分配长度为 toIndex - fromIndex 的结果数组,然后把每个选中的值复制到结果数组中对应的位置。

下面这个完整的程序从一个整数数组中选取索引 1 到 3 的元素:

import java.util.Arrays;

public class ArraySliceLoop {
    static int[] slice(int[] values, int fromIndex, int toIndex) {
        int[] result = new int[toIndex - fromIndex];
        for (int i = 0; i < result.length; i++) {
            result[i] = values[fromIndex + i];
        }
        return result;
    }

    public static void main(String[] args) {
        int[] values = {10, 20, 30, 40, 50};
        int[] part = slice(values, 1, 4);
        System.out.println(Arrays.toString(part));
    }
}

结果是一个独立的数组。循环计数器 i 针对结果数组从零开始,而 fromIndex + i 则定位源数组中对应的元素。调用该方法时,应传入在源数组范围内描述有效范围的索引。

在 Java 中使用 copyOfRange() 方法对数组进行切片

Arrays.copyOfRange() 提供了一种从某个范围直接创建新数组的方法。该方法属于 java.util.Arrays;它的 from 参数包含在内,to 参数不包含在内。确切的 API 名称中包含 copyOfRange()。

下面这个程序复制索引 1、2 和 3 的元素:

import java.util.Arrays;

public class ArraySliceCopyOfRange {
    public static void main(String[] args) {
        int[] values = {10, 20, 30, 40, 50};
        int[] part = Arrays.copyOfRange(values, 1, 4);
        System.out.println(Arrays.toString(part));
    }
}

返回的数组包含选中的值,修改它不会替换原始数组中的元素。对于 int[] 等基本类型数组,复制的是值本身。对于对象数组,数组是新建的,但其中的元素仍是对相同对象的引用。

在 Java 中使用流对数组进行切片

流可以通过把索引流映射回源数组来选取数组中的值。把这种方式当作一种可选方案:只有当项目配置的 Java 运行时支持所需的流 API 时才使用它。这里不预设任何运行时版本。

这个操作可以写成 IntStream.range(fromIndex, toIndex).map(i -> values[i]).toArray()。IntStream.range 使用包含起始、排除结尾的约定,因此与其它方法遵循相同的范围约定。这个紧凑的表达式只是一个示意;在实际使用之前,请先对照项目声明的 Java 运行时确认 API 可用性。当运行时未知时,循环或 Arrays.copyOfRange() 是直接明了的替代方案。

在 Java 中使用 System.arraycopy 方法对数组进行切片

System.arraycopy 把指定数量的元素从一个数组复制到另一个数组。先创建一个长度为 toIndex - fromIndex 的目标数组,然后传入源位置、目标位置和要复制的元素数量。

import java.util.Arrays;

public class ArraySliceSystemArraycopy {
    public static void main(String[] args) {
        int[] values = {10, 20, 30, 40, 50};
        int fromIndex = 1;
        int toIndex = 4;
        int[] part = new int[toIndex - fromIndex];
        System.arraycopy(values, fromIndex, part, 0, part.length);
        System.out.println(Arrays.toString(part));
    }
}

这些参数依次是源数组、源位置、目标数组、目标位置和要复制的元素数量。这里目标位置为零,因此选中的源范围会从目标数组的第一个位置开始填满 part。源数组和目标数组的元素类型必须兼容,位置和长度也必须同时适合两个数组。

在 Java 中使用 asList 和 subList 对数组进行切片

对于引用类型数组,Arrays.asList() 会创建一个由该数组支撑的固定大小列表。subList(fromIndex, toIndex) 返回所选列表范围的视图。当结果应当是一个独立数组时,把该视图转换成数组。

import java.util.Arrays;
import java.util.List;

public class ArraySliceSubList {
    public static void main(String[] args) {
        String[] values = {"red", "green", "blue", "gold"};
        List<String> list = Arrays.asList(values);
        List<String> selected = list.subList(1, 3);
        String[] part = selected.toArray(new String[0]);
        System.out.println(Arrays.toString(part));
    }
}

Arrays.asList() 返回的列表大小固定:你可以替换某个元素,但不能添加或删除列表条目。子列表是与该列表绑定的视图。调用 toArray() 会创建一个独立的数组,尽管对象元素本身仍是对相同对象的引用。这种方式适用于 String[] 等引用类型数组;基本类型数组需要使用上面展示的循环、copyOfRange() 或 System.arraycopy 等处理基本类型数组的方法。

结论

当你想看清或定制逐元素复制的过程时,使用循环。想要直接复制数组时选择 Arrays.copyOfRange(),或者在需要指定目标位置和复制长度时选择 System.arraycopy。当项目的 Java 运行时支持所需的 API 时,流提供了基于索引的选取方式。对于引用类型数组,asList 和 subList 提供了列表视图;调用 toArray() 即可获得一个独立的结果数组。