Skip to content

Streams and Lambdas

Brian Burton edited this page Nov 12, 2017 · 7 revisions

Note: Content of this page applies only to versions 2.0.0 and above.

Java Streams

Java 8 added Streams to the standard library and lambda expressions to the language. Starting with version 2.x these are now directly supported by Javaimmutable 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 IterableStreamable. 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 IterableStreamable interface. In addition several collections have view methods that return IterableStreamable objects for operating on a slice of the collection. For example, JImmutableMap 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. And JImmutableMultiset defines its own view methods:

  • entries() returns a view for operating on JImmutableMap.Entry 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

Utility Methods in IterableStreamable

While streams are extremely powerful they do imply some overhead that you may not need all of the time. For cases where you simply want to perform some operations on a collection within a single thread you can use these additional utility methods defined by IterableStreamable. Most of them operate on all of the values in the collection by passing each value to a lambda expression to yield a result.

Collecting Values

The collect() methods can be used to copy values from the collection into another. All of them accept a destination collection to receive the values. Two of the variations also accept a Predicate used to determine which values should be copied.

  JImmutableList<String> source = JImmutables.list("axle", "wheel", "apple", "wall");
  JImmutableSet<String> copied = source.collect(JImmutables.set());
  // copied now contains "apple", "axle", "wall", and "wheel"

  copied = source.collect(2, JImmutables.set());
  // copied now contains "axle" and "wheel"

  copied = source.collect(JImmutables.set(), str -> str.startsWith("a"));
  // copied now contains "axle" and "apple"

  copied = source.collect(1, JImmutables.set(), str -> str.startsWith("w")); 
  // copied now contains "wheel"

Transforming Values

The transform() methods are similar to collect() but they use a lambda to transform each element of the collection before adding the result to the collection. The transformSome() methods are similar but the lambdas return Holder objects instead of values. Empty Holders are ignored. Non-empty Holders have their value copied to the collection.

  JImmutableSet<String> source = JImmutables.set("axle", "wheel", "apple", "wall");
  JImmutableList<String> transformed = source.collect(JImmutables.list(), str -> str.toUpper());
  // transformed now contains "APPLE", "AXLE", "WALL", and "WHEEL"

  transformed = source.transform(2, JImmutables.list(), str -> str.substring(1));
  // transformed now contains "xle" and "heel"

  transformed = source.transformSome(JImmutables.list(), str -> str.length() == 4 ? Holders.of() : Holders.of(str));
  // transformed now contains "wheel" and "apple"

  transformed = source.transformSome(1, JImmutables.list(), str -> str.length() == 4 ? Holders.of() : Holders.of(str));
  // transformed now contains "wheel"

Partitioning a Collection

The partition() method uses a predicate to split a copy collection's values to two other collections. One receives the values for which the predicate returned true. The other receives those for which the predicate returns false. The resulting collections are packaged into a Partitions object and returned to the caller.

  JImmutableSet<String> source = JImmutables.set("axle", "wheel", "apple", "wall");
  Partitions parts = source.partition(JImmutables.set(), JImmutables.set(), str -> str.startsWith("a"));
  // parts.getMatched() contains "axle" and "apple"
  // parts.getUnmatched() contains "wheel" and "wall"

Miscellaneous

The count() method returns the number of elements or the number of elements matching a Predicate. The first() method returns the first element matching a Predicate. The allMatch() and anyMatch() methods test the elements and return the expected boolean result.

    JImmutableSet<String> source = JImmutables.insertOrderSet("axle", "wheel", "apple", "wall");
    assertEquals(4, source.count());
    assertEquals(3, source.count(str -> str.contains("a")));
    assertEquals("axle", source.first(str -> str.startsWith("a")));
    assertEquals(true, source.anyMatch(str -> str.startsWith("a")));
    assertEquals(false, source.allMatch(str -> str.startsWith("a")));

Clone this wiki locally