Interface Stream
A sequence of elements supporting sequential and parallel aggregate operations. The following example illustrates an aggregate operation using Stream and IntStream :
int sum = widgets.stream() .filter(w -> w.getColor() == RED) .mapToInt(w -> w.getWeight()) .sum();
In this example, widgets is a Collection
In addition to Stream , which is a stream of object references, there are primitive specializations for IntStream , LongStream , and DoubleStream , all of which are referred to as «streams» and conform to the characteristics and restrictions described here.
To perform a computation, stream operations are composed into a stream pipeline. A stream pipeline consists of a source (which might be an array, a collection, a generator function, an I/O channel, etc), zero or more intermediate operations (which transform a stream into another stream, such as filter(Predicate) ), and a terminal operation (which produces a result or side-effect, such as count() or forEach(Consumer) ). Streams are lazy; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed.
A stream implementation is permitted significant latitude in optimizing the computation of the result. For example, a stream implementation is free to elide operations (or entire stages) from a stream pipeline — and therefore elide invocation of behavioral parameters — if it can prove that it would not affect the result of the computation. This means that side-effects of behavioral parameters may not always be executed and should not be relied upon, unless otherwise specified (such as by the terminal operations forEach and forEachOrdered ). (For a specific example of such an optimization, see the API note documented on the count() operation. For more detail, see the side-effects section of the stream package documentation.)
Collections and streams, while bearing some superficial similarities, have different goals. Collections are primarily concerned with the efficient management of, and access to, their elements. By contrast, streams do not provide a means to directly access or manipulate their elements, and are instead concerned with declaratively describing their source and the computational operations which will be performed in aggregate on that source. However, if the provided stream operations do not offer the desired functionality, the BaseStream.iterator() and BaseStream.spliterator() operations can be used to perform a controlled traversal.
A stream pipeline, like the «widgets» example above, can be viewed as a query on the stream source. Unless the source was explicitly designed for concurrent modification (such as a ConcurrentHashMap ), unpredictable or erroneous behavior may result from modifying the stream source while it is being queried.
- must be non-interfering (they do not modify the stream source); and
- in most cases must be stateless (their result should not depend on any state that might change during execution of the stream pipeline).
Such parameters are always instances of a functional interface such as Function , and are often lambda expressions or method references. Unless otherwise specified these parameters must be non-null.
A stream should be operated on (invoking an intermediate or terminal stream operation) only once. This rules out, for example, «forked» streams, where the same source feeds two or more pipelines, or multiple traversals of the same stream. A stream implementation may throw IllegalStateException if it detects that the stream is being reused. However, since some stream operations may return their receiver rather than a new stream object, it may not be possible to detect reuse in all cases.
Streams have a BaseStream.close() method and implement AutoCloseable . Operating on a stream after it has been closed will throw IllegalStateException . Most stream instances do not actually need to be closed after use, as they are backed by collections, arrays, or generating functions, which require no special resource management. Generally, only streams whose source is an IO channel, such as those returned by Files.lines(Path) , will require closing. If a stream does require closing, it must be opened as a resource within a try-with-resources statement or similar control structure to ensure that it is closed promptly after its operations have completed.
Stream pipelines may execute either sequentially or in parallel. This execution mode is a property of the stream. Streams are created with an initial choice of sequential or parallel execution. (For example, Collection.stream() creates a sequential stream, and Collection.parallelStream() creates a parallel one.) This choice of execution mode may be modified by the BaseStream.sequential() or BaseStream.parallel() methods, and may be queried with the BaseStream.isParallel() method.
- Three Ways to Sum a Stream of Numbers
- Option 1 – Using mapToInt on the Stream
- Sum a Java Stream as a long or double
- Option 2 – Sum a Java Stream by reducing the stream
- Option 3 – Using Collectors.summingInt
- Sum a stream obtained from a Map
- Conclusion
- References
- Share This Post Share this content
- You Might Also Like
- Java Stream Distinct with examples
- Java Stream Distinct By Property
- Java 8 Streams are not completely Lazy!
- This Post Has 2 Comments
Three Ways to Sum a Stream of Numbers
In this post, we will see three ways to sum a stream of numbers using Java streams. We will use a stream created from a list of integers and sum the integers using various approaches.
Option 1 – Using mapToInt on the Stream
A stream can contain elements of any type, whereas an IntStream represents a sequence of primitive int-valued elements. We can convert a stream of arbitrary type to an IntStream by using the mapToInt method on the stream.
IntStream mapToInt(ToIntFunction mapper)
The mapToInt method takes a ToIntFunction. A ToIntFunction is a functional interface which maps any element to a primitive int value. It has a single method called applyAsInt as shown below.
When we pass an instance of ToIntFunction to the mapToInt method, it applies this function to each element in the stream and returns an IntStream. An IntStream has a sum() method to sum all the elements in the IntStream (which are primitive integers).
List integers = List.of(1, 2, 3, 4, 5); int sum = integers.stream() .mapToInt(Integer::intValue) .sum();
Note: List.of method was added in Java 9 as part of the many Collection factory methods. If you are using Java 8 or lower, use Arrays.asList instead.
In the above example, calling stream() on the collection returns a stream of integers (Stream ). Integer::intValue is a method reference for the lambda expression `element -> element.intValue()`. It unboxes an Integer to a primitive int and thus it satisfies the ToIntFunction method signature (i.e., it takes an Integer object and returns a primitive int). Since we have an IntStream, we call the sum method on it to find the sum of elements in the (Int)Stream.
However, there is another way we could have written the mapToInt to make advantage of the automatic unboxing.
int sum = integers.stream() .mapToInt(e -> e) .sum();
The lambda expression e -> e returns the same Integer instance. But, under the hood, automatic unboxing takes place and hence this is equivalent to calling the intValue method on the Integer object.
Sum a Java Stream as a long or double
We can use the mapToLong and mapToDouble methods to map an element to a long and a double, respectively. It will return a LongStream and a DoubleStream, respectively. Then, calling sum() on the LongStream or DoubleStream will return a double or a long.
- mapToLong takes a ToLongFunction. The ToLongFunction has a single method called applyAsLong to map an element to a primitive long.
- mapToDouble takes a ToDoubleFunction. The ToDoubleFunction has a single method called applyAsDouble to map an element to a primitive double.
long sumAsLong = integers.stream() .mapToLong(Integer::intValue) .sum(); double sumAsDouble = integers.stream() .mapToDouble(Integer::intValue) .sum();
Option 2 – Sum a Java Stream by reducing the stream
The second way by which we can sum a stream of numbers is to use the reduce method on the stream. The reduce method performs a reduction on the stream elements using the provided
The identity element when passed to the accumulator function with some other element (say e) must return the other element (e) as the result.
Example: The identity element for addition is 0, because 0 + e (any element) gives us e. The identity for multiplication is 1 (1 x e = e).
The accumulator function is represented by a BinaryOperator. A BinaryOperator is a BiFunction i.e., it takes two elements of a type and returns a single result of the same type.
int sum = integers.stream() .reduce(0, (a, b) -> a + b);
We pass an identity value of 0 for the accumulator function. The accumulator function takes two elements of type int and returns the sum of them (an int). Since the Integer’s sum method takes two integers and returns the sum of it, we can use a method reference in its place, as shown below.
int sum = integers.stream() .reduce(0, Integer::sum);
I have a separate post on Java Stream reduce method where I talk in depth on the three overloaded Stream#reduce methods.
Option 3 – Using Collectors.summingInt
The Collectors.summingInt is a Collector that sums the primitive integers. It accepts a ToIntFunction (the same type passed for the mapToInt method) to convert an element in the stream to a primitive int.
We can use the Stream#collect along with Collectors.summingInt as shown below.
int sum = integers.stream() .collect(Collectors.summingInt(Integer::intValue));
However, this is a roundabout way of computing a stream’s sum. The Intellij IDE suggests to refactor the above to use mapToInt (the first method).
Sum a stream obtained from a Map
So far, we have learnt three ways to sum a stream of numbers where the stream was created from a list. If we had a map, then we just have to create a stream of integers out of the map’s values or keys and use any of the methods we’ve seen.
Let us say we have the below map.
Map map = Map.of( 1, "abc", 2, "defg", 3, "pqrs", 4, "uvw", 5, "xyz");
We want to find the sum of the lengths of all even length strings.
int result = map.values().stream() .filter(string -> string.length() % 2 == 0) .mapToInt(String::length) .sum(); System.out.println(result); //8
We create a Stream from the map values, filter the strings whose length is of even length. Then in the mapToInt, we map a string element into an integer, i.e., the length of the string. Finally, we call sum() on the stream to get the result.
Conclusion
In this post, we learnt three ways to sum a stream of integers in Java. We looked at the mapToInt, reduce, and collect methods from the Stream.
References
Share This Post Share this content
You Might Also Like
Java Stream Distinct with examples
November 2, 2020
Java Stream Distinct By Property
November 9, 2020
Java 8 Streams are not completely Lazy!
This Post Has 2 Comments
in the last example, the result of (int result) will be 4.
only one row matches after filtering on key 2. System.out.println(result); // 4