Java Stream.of(item.collection(Collectors.toSomeCollection())比新的SomeCollection(Arrays.asList(item))快吗?

Java Stream.of(item.collection(Collectors.toSomeCollection())比新的SomeCollection(Arrays.asList(item))快吗?,java,collections,java-stream,Java,Collections,Java Stream,Java8提供了 Stream.of(item).collect(Collectors.toSomeCollection()) e、 g.Stream.of(“abc”).collect(Collectors.toSet())。在窗帘前避开相对昂贵的新操作员是否比 new SomeCollection(Arrays.asList(item)) e、 g.新哈希集(Arrays.asList(“abc”)) 我确信使用new初始化不同集合的成本不同(哈希集在准备使用之前需要一个哈希表,数组列表

Java8提供了

Stream.of(item).collect(Collectors.toSomeCollection())
e、 g.
Stream.of(“abc”).collect(Collectors.toSet())
。在窗帘前避开相对昂贵的
操作员是否比

new SomeCollection(Arrays.asList(item))
e、 g.
新哈希集(Arrays.asList(“abc”))

我确信使用
new
初始化不同集合的成本不同(哈希集在准备使用之前需要一个哈希表,数组列表需要一个分配的数组,而链表不需要这些)

我试图弄清楚流相关类是否在内部避免了
new
,但OpenJDK代码很难理解。如果我把
Stream.of
看作一个已经初始化的管道和使用可重用函数的收集器,我认为它们可以


是否会是另一种情况?

一个
列表
集合
映射
的收集器将最终创建一个
列表
集合
映射
实例。
例如,下面是
收集器.toSet()
主体:

public static <T>
Collector<T, ?, Set<T>> toSet() {
    return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_UNORDERED_ID);
}
公共静态
收集器toSet(){
返回新的CollectorImpl((供应商)HashSet::new,Set::add,
(左,右)->{left.addAll(右);返回left;},
(未订购);;
}
因此,您将永远不会使用
新的
运算符语句。

Stream.of()
允许操作


我认为这是使用它的唯一有效理由。
一个
列表
集合
映射
的收集器将最终创建一个
列表
集合
映射
实例。
例如,下面是
收集器.toSet()
主体:

public static <T>
Collector<T, ?, Set<T>> toSet() {
    return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_UNORDERED_ID);
}
公共静态
收集器toSet(){
返回新的CollectorImpl((供应商)HashSet::new,Set::add,
(左,右)->{left.addAll(右);返回left;},
(未订购);;
}
因此,您将永远不会使用
新的
运算符语句。

Stream.of()
允许操作

我认为这是使用它的唯一有效理由。
让我们衡量(使用)并找出哪一个“更快”:

@BenchmarkMode({ Mode.AverageTime })
@Warmup(iterations = 10)
@Measurement(iterations = 10)
@Fork(1)
public class MyBenchmark {

    private static final int ITERATIONS = 10_000_000;

    @Benchmark
    public void baseLine(Blackhole bh) {
        for (int i = 0; i < ITERATIONS; i++) {
            Set<String> s = new HashSet<>();
            s.add("A");
            bh.consume(s);
        }
    }

    @Benchmark
    public void asList(Blackhole bh) {
        for (int i = 0; i < ITERATIONS; i++) {
            Set<?> s = new HashSet<>(Arrays.asList("A"));
            bh.consume(s);
        }
    }

    @Benchmark
    public void collectStream(Blackhole bh) {
        for (int i = 0; i < ITERATIONS; i++) {
            Set<?> s = Stream.of("A").collect(Collectors.toSet());
            bh.consume(s);
        }
    }

    @Benchmark
    public void setOf(Blackhole bh) {
        for (int i = 0; i < ITERATIONS; i++) {
            Set<?> s = Set.of("A");
            bh.consume(s);
        }
    }

    @Benchmark
    public void singletonCollection(Blackhole bh) {
        for (int i = 0; i < ITERATIONS; i++) {
            Set<?> s = Collections.singleton("A");
            bh.consume(s);
        }
    }

}
因此,在您提到的2种方法中,使用
asList
似乎更快。如果只有一个元素,则可以使用
Collections.singleton*
(如@AndyTurner所建议的)。Java 9接口工厂方法可以使用任意数量的元素,但也针对单个元素进行了优化。

让我们测量(使用)并找出哪一个“更快”:

@BenchmarkMode({ Mode.AverageTime })
@Warmup(iterations = 10)
@Measurement(iterations = 10)
@Fork(1)
public class MyBenchmark {

    private static final int ITERATIONS = 10_000_000;

    @Benchmark
    public void baseLine(Blackhole bh) {
        for (int i = 0; i < ITERATIONS; i++) {
            Set<String> s = new HashSet<>();
            s.add("A");
            bh.consume(s);
        }
    }

    @Benchmark
    public void asList(Blackhole bh) {
        for (int i = 0; i < ITERATIONS; i++) {
            Set<?> s = new HashSet<>(Arrays.asList("A"));
            bh.consume(s);
        }
    }

    @Benchmark
    public void collectStream(Blackhole bh) {
        for (int i = 0; i < ITERATIONS; i++) {
            Set<?> s = Stream.of("A").collect(Collectors.toSet());
            bh.consume(s);
        }
    }

    @Benchmark
    public void setOf(Blackhole bh) {
        for (int i = 0; i < ITERATIONS; i++) {
            Set<?> s = Set.of("A");
            bh.consume(s);
        }
    }

    @Benchmark
    public void singletonCollection(Blackhole bh) {
        for (int i = 0; i < ITERATIONS; i++) {
            Set<?> s = Collections.singleton("A");
            bh.consume(s);
        }
    }

}
因此,在您提到的2种方法中,使用
asList
似乎更快。如果只有一个元素,则可以使用
Collections.singleton*
(如@AndyTurner所建议的)。Java 9接口工厂方法可以使用任意数量的元素,但也针对单个元素进行了优化。

理论上,
Stream.of(item).collect(Collectors.toSomeCollection())
可以返回更有效的集合,因为故意未指定确切的集合类型

在实践中,有一些障碍阻碍了这一点。例如,收集器必须确保容器能够累积元素,而无需任何关于流特征的提示,即使它们是事先已知的,如
stream.of(item)
示例中已知的单个元素。收集器可以提供中间类型的提供者,该中间类型在最后转换为不同的结果类型,这可能会提高内存效率,但需要额外的CPU时间


这导致了一种情况,例如,
Arrays.asList(stream.toArray())
可能比
stream.collect(Collectors.toList())
更有效,因为
toArray()
操作可以在可预测的情况下访问流大小。但是对于通过
stream.of(item[,items])
创建的没有中间操作的流,这显然不会比
Arrays.asList(item[,items])
更有效,后者首先创建了一个
列表,而没有流机制

但是对于单个项,
Collections.singletoList(item)
或Java9的
List.of(item)
更有效,因为它们根本不需要处理数组


对于创建
集合
,差异甚至更大,如中的实际数字所示

为了理解它们,我们必须认识到,在每个“基线”、“asList”和“collectStream”变体中,都创建了一个
HashSet
。即使在当前的Java 9中,
HashSet
仍然作为
HashMap
的包装器实现,这减少了代码大小,但没有增加堆大小,当然也不会提高效率

因此,在所有这三种变体中,单个元素集实例由
HashSet
实例、
HashMap
实例、数组(条目)实例和
Map.Entry
实现实例组成。这一重量在所有三种型号中都有

在“asList”变量中的
HashSet
构造过程中,会创建varargs参数的数组,并
数组。asList
会在其周围创建一个包装器

相比之下,“collectStream”变量可以解决一个优化的
变量的单个参数
Stream.的问题,但它仍然需要创建一个保存该项的流生成器(充当
拆分器
)和一个在其上操作的
实现实例

此外,“asList”和“collectStream”都将在内部处理条件,因为将在源上进行迭代,无论其形式如何,以将所有元素添加到
HashSet
,而在“基线”变量中,知识