Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to create Stream implementation that counts their elements in a single operation

Q: Is it possible to create Stream implementation that counts their elements in a single operation rather than counting each and every element in the stream?

I came to this though when i tried to compare two methods on a list :

  • size()

  • count()

Stream::count terminal operation counts the number of elements in a Stream. The complexity of the operation is often O(N), meaning that the number of sub-operations is proportional to the number of elements in the Stream.

List::size method has a complexity of O(1), which means that regardless of the number of elements in the List, the size() method will return in constant time.

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

size() took a relative less time than count(),so is there any possible way to create Stream implementation that counts their elements in a single operation and make a complexity of O(1) ??


Edit Article to answer Yes:

It is possible to create Stream implementation that counts their elements in a single operation O(1) rather than counting each and every element in the stream. This can improve performance significantly, especially for streams with many elements.

like image 298
Vishwa Ratna Avatar asked Apr 11 '19 09:04

Vishwa Ratna


People also ask

How do you implement a count in Java?

The counting() method of the Java 8 Collectors class returns a Collector accepting elements of type T that counts the number of input elements.

When performing operations on a stream will it affect the original stream?

A stream can be composed of multiple functions that create a pipeline that data that flows through. This data cannot be mutated. That is to say the original data structure doesn't change. However the data can be transformed and later stored in another data structure or perhaps consumed by another operation.

Which of the following methods can we use to operate on a stream and transform it into another stream?

Using the Stream API and the map method, we can transform elements in a stream to another object. Instead of using the map method, we can also write a custom Collector and transform the elements when we use the collect method as a terminal operation of the stream.

Can we have stream without terminal operation?

Non-Terminal Operations. The non-terminal stream operations of the Java Stream API are operations that transform or filter the elements in the stream. When you add a non-terminal operation to a stream, you get a new stream back as result.


1 Answers

This is already happening in Java 9 and newer (considering the OpenJDK implementation which is also the base for Oracle’s JDK).

If you want a similar operation, you can use, e.g.

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;
}

You can check that it won’t process all elements with

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)));

whereas inserting a filter operation like

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

will make the count unpredictable and require a traversal.

As said above, in JDK 9 or newer, you can simple use

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

and

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

to see that the traversal does not happen when the count is predictable.

like image 96
Holger Avatar answered Sep 28 '22 05:09

Holger