Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.

Java 8 Revealed: Lambdas, Default Methods and Bulk Data Operations

What if you no longer had to redeploy your Java code to see changes? The choice is yours. In just a few clicks you can Say Goodbye to Java Redeploys forever.

Terminating operations

Usually, dealing with a stream will involve these steps:

  1. Obtain a stream from some source.
  2. Perform one or more intermediate operations, like filter, map, etc.
  3. Perform one terminal operation.

A terminal operation must be the final operation invoked on a stream. Once a terminal operation is invoked, the stream is “consumed” and is no longer usable.

There are several types of terminal operations available:

  • reducers like reduce(..), count(..), findAny(..), findFirst(..) terminate stream processing. Depending on the intention, the terminal operation can be a short-circuiting one. For instance, findFirst(..) will terminate the stream processing as soon as it encounters a matching element.
  • collectors, as the name implies, are for collecting the processed elements into a resulting collection.
  • forEach performs some action for each element in the stream.
  • iterators are the good ‘ol way to work with collections if none of the options above satisfies our needs.

The most interesting terminal operation type are the so-called “collectors”.


While stream abstraction is continuous by its nature, we can describe the operations on streams but to acquire the final results we have to collect the data somehow. The Stream API provides a number of so-called “terminal” operations. The collect() method is one of the terminal operations that allow us to collect the results:

List students =
    .filter(p -> p.getAge() > 18)
    .collect(new Collector<Student, List>() { ... });

Fortunately, in most cases you wouldn’t need to implement the Collector interfaces yourself. Instead, there’s a Collectors utility class for convenience:

List students =
    .filter(p -> p.getAge() > 18)

Or in case if we would like to use a specific collection implementation for collecting the results:

List students =
    .filter(p -> p.getAge() > 18)

Parallel and sequential

One interesting feature of the new Stream API is that it doesn’t require to operations to be either parallel or sequential from beginning till the end. It is possible to start consuming the data concurrently, then switch to sequential processing and back at any point in the flow:

List students =
    .filter(p -> p.getAge() > 18)  // filtering will be performed concurrently

The cool part here is that the concurrent part of data processing flow will manage itself automatically, without requiring us to deal with the concurrency issues.

Summary, Conclusion and a Goodbye Comic

In the current report we have covered three main topics in the upcoming Java 8:

  1. Lambda expressions
  2. Default methods
  3. Bulk data operations for Java collections

As we have seen, lambdas are greatly improve readability of the code and making Java the language more expressive, especially when we move on to new Stream API. Default methods, in turn, are essential for API evolution, connecting the Collections API with lambdas for our convenience. Nevertheless, the ultimate goal of all this new features is to introduce parallel libraries and to seamlessly take advantage of the multi-core hardware.

I’m gonna make this a really short conclusion here as well, which basically comes down to this: while the JVM itself is a great piece of engineering, whether you want to admit it or not, the Java platform is still alive and kicking. These new changes will allow us to leverage the platform and the language in a much more efficient way, and will ideally give the critics of Java a bit more substance to chew on.