根据Collection.toArray(T [] array)合同将Java流转换为数组

时间:2018-11-13 16:43:51

标签: java arrays collections java-stream

请大家先阅读这个问题,然后再看到“流”和“数组”,并假设该问题是重复的。我知道如何将流转换为数组。这个问题非常具体:如何遵守Collection.toArray(T[] array)的合同。所有这些人都说这是重复的:如果答案足够大,其他答案是否会重用现有数组?如果现有数组太大,它们是否用null标记元素? (不,不。)

我知道Java Stream<>接口提供了几种将流转换为数组的方法。但是Collection.toArray(T[] array)方法有点不同。 (当时)它有一些聪明的要求,包括:

  • 如果传递的数组足够大,则必须使用传递的数组;否则,必须创建一个新的。
  • 如果数组大于必需的数组,则必须在最后一个元素之后添加null

因此,如果我的Collection<T>实现从某个Stream<FooBar>检索其值(使用转换策略转换为T的转换策略,该如何从我的流转换为{{ 1}}?

没有太多的思想,看来我必须这样做:

Collection.toArray(T[] array)

但是还有其他更简洁的方法吗?如果可以的话,有什么方法可以将流直接传输到给定的数组中? @Override public <T> T[] toArray(T[] array) { try (final Stream<FooBar> stream = getStream()) { T[] result = stream.map(converter::toT).toArray(length -> (T[])Array.newInstance(array.getClass(), length)); if(result.length <= array.length) { System.arraycopy(result, 0, array, 0, result.length); if(result.length < array.length) { array[result.length] = null; } result = array; } } return result; } API是否已经提供了类似的功能:按照Stream<> API的期望创建数组?

1 个答案:

答案 0 :(得分:1)

非常推荐的读物是文章Arrays of Wisdom of the Ancients

简而言之,与直觉相反,将预大小的数组传递给Collections.toArray(T[])方法的效率要比传递零大小的数组低,后者仅用于确定结果类型,但允许集合进行分配结果数组。

这就是Java 11新的default方法<T> T[] toArray​(IntFunction<T[]> generator)不使用该函数分配集合大小的数组而是分配零大小的数组以传递给<T> T[] toArray​(T[] a)的原因。

因此,值得反思的是,您是否真的想要这种方法的合同或您真正想要优化的实际用例(因为您无法一次提供全部服务)。

例如考虑到传递零大小的数组仍然是最有效的选择,因此您可以针对这种情况进行优化

@Override
public <T> T[] toArray(T[] array) {
    T[] template = array.length == 0? array: Arrays.copyOf(array, 0);
    try(Stream<FooBar> stream = getStream()) {
        T[] result = stream.map(converter::toT)
            .toArray(length -> Arrays.copyOf(template, length));
        if(result.length > array.length) return result;
        System.arraycopy(result, 0, array, 0, result.length);
        if(result.length < array.length) array[result.length] = null;
        return array;
    }
}

请注意,由于要实现Collection而必须实现该方法时,JDK中有很多有用的抽象基类已经提供了实现。

例如,当您不实现集合时,甚至可以利用这种实现。

public <T> T[] toArray(T[] array) {
    try(final Stream<FooBar> stream = getStream()) {
        return new AbstractCollection<XYZ>() {
            public Iterator<XYZ> iterator() {
                return stream.map(converter::toT).iterator();
            }
            public int size() { return 0; } // don't know beforehand
        }.toArray(array);
    }
}

您必须用XYZ方法的返回类型替换converter.toT(FooBar)

哪个问题引出了更大的问题,如何将converter::toT转换为正确的类型而又不真正知道T是什么。