请大家先阅读这个问题,然后再看到“流”和“数组”,并假设该问题是重复的。我知道如何将流转换为数组。这个问题非常具体:如何遵守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的期望创建数组?
答案 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
是什么。