Skip to content

Streams and Lambdas

Brian Burton edited this page May 21, 2023 · 7 revisions

Streams and Lambdas

Java Streams

Java 8 added Streams to the standard library and lambda expressions to the language. These are directly supported by Javimmutable collections. Streams allow you to operate on all elements of a collection either serially or in parallel. Javimmutable supports Streams through a new interface named IStreamable. This interface defines two methods for creating Streams:

  Stream stream();
  Stream parallelStream();

The stream() method returns a Stream for processing the collection in a single thread. The parallelStream() method returns a Stream for processing the collection using multiple threads. (Using parallelStream() is similar to using stream().parallel()).

All collections implement the IStreamable interface. In addition several collections have view methods that return IStreamable objects for operating on a slice of the collection. For example, IMap defines these view methods:

  • keys() returns a view for operating on just the keys in the map
  • values() returns a view for operating on just the values in the map

Other collections define these view methods as well. IMultiset defines its own view methods:

  • entries() returns a view for operating on IMapEntry objects containing each value plus its count
  • ocurrences() returns a view for operating on all values in the multiset with each value appearing its count times in the stream

Collecting Values

Streams can be used to build collections efficiently using the Collector implementations defined in ICollectors. See [Collections Overview] for details.

IList<String> source = ILists.of("axle", "wheel", "apple", "wall");
ISet<String> copied = source.stream().collect(ICollectors.toSet());
// copied now contains "apple", "axle", "wall", and "wheel"

Utility Methods in IStreamable

IStreamable extends Iterable so it inherits the helper methods from that interface. It also provides some of its own. These operate on all of the values of the collection without using a stream.

forEach and forEachThrows

The forEach() method passes every item in the collection to a provided lambda. The forEachThrows() does the same but accepts that lambda that can throw a checked exception.

indexedForEach and indexedForEachThrows

The indexedForEach() method passes every item in the collection along with its index to a provided lambda. The provided index starts at zero and incements by one for each item. The indexedForEachThrows() does the same but accepts that lambda that can throw a checked exception.

reduce and reduceThrows

Sometimes you need to process all elements to produce a single result. For example you may want to compute the sum of a list of integers. The IStreamable interface provides a method for doing so.

The reduce() method accepts a starting value and a lambda that accepts the current value plus an element from the collection. The lambda is invoked once for each element. The first call to the lambda receives the initial value and the first element. All other calls receive the result of the previous call and an element. The result of the reduce() call is the final value returned by the lambda.

IList<Integer> values = ILists.of(1, 2, 3, 4, 5, 6, 7, 8);
assertEquals(36, (int)values.reduce(0, (s,x) -> s + x));        // 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8
assertEquals(-18, (int)values.reduce(18, (s,x) -> s - x));      // 18 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8
assertEquals(16, (int)values.reduce(0, (s,x) -> s + (x / 2)));  // 0 + 0 + 1 + 1 + 2 + 2 + 3 + 3 + 4

An equivalent method, reduceThrows is provided that accepts a function that can throw a checked exception.