是否可以创建在单个操作中计算其元素的Stream实现

时间:2019-04-11 09:15:48

标签: java java-8 java-stream

问:是否有可能创建一个Stream实现,该实现在单个操作中对元素进行计数,而不是对流中的每个元素进行计数?

但是,当我尝试比较列表中的两种方法时,我想到了这一点:

  • size()

  • count()

Stream::count终端操作计算Stream中元素的数量。操作的复杂度通常为 O(N),这意味着子操作的数量与 Stream 中的元素数量成正比。

List::size方法的复杂度为 O(1),这意味着无论List中元素的数量如何,size()方法都会在恒定时间内返回

   List<Integer> list = IntStream.range(0, 100).boxed().collect(toList());
    System.out.println(list.size());
    System.out.println(list.stream().count());

size()花费的时间比count()短,所以有什么可能的方法来创建Stream实现,该实现在单个操作中对元素进行计数并使其复杂度为 O(1) ??


编辑 Article to answer Yes

  

可以创建Stream个实现,将其数量   一次操作 O(1)中的元素,而不是对每个元素进行计数   流中的每个元素。这样可以提高性能   尤其是对于包含许多元素的流而言。

1 个答案:

答案 0 :(得分:8)

这已经在Java 9和更高版本中发生(考虑到OpenJDK实现,这也是Oracle JDK的基础)。

如果您想要类似的操作,则可以使用例如

public static long count(BaseStream<?,?> s) {
    Spliterator<?> sp = s.spliterator();
    long c = sp.getExactSizeIfKnown();
    if(c >= 0) return c;
    final class Counter implements Consumer<Object>,
        IntConsumer, LongConsumer, DoubleConsumer { // avoid boxing where possible
        long count;
        public void accept(Object t) { count++; }
        public void accept(int value) { count++; }
        public void accept(long value) { count++; }
        public void accept(double value) { count++; }
    }
    Counter c = new Counter();
    sp.forEachRemaining(c);
    return c.count;
}

您可以检查它不会处理所有元素

System.out.println(count(IntStream.range(0, 100).peek(System.out::println)));
System.out.println(count(Stream.of("a", "b", "c").peek(System.out::println)));

而插入filter这样的操作

System.out.println(count(Stream.of("a", "b", "c")
    .peek(System.out::println).filter(x -> true)));

将使计数不可预测,并且需要遍历。

如上所述,在JDK 9或更高版本中,您可以轻松使用

System.out.println(Stream.of("a", "b", "c").peek(System.out::println).count());

System.out.println(Stream.of("a", "b", "c")
    .peek(System.out::println).filter(x -> true).count());

看到在可预测的计数时不会发生遍历。