Modern Java A Guide To 8 Java8

User Manual:

Open the PDF directly: View PDF PDF.
Page Count: 90

DownloadModern Java - A Guide To 8 Modern-java-a-guide-to-java8
Open PDF In BrowserView PDF
Modern Java - A Guide to Java 8

Table of Contents
Introduction

0

Modern Java - A Guide to Java 8

1

Java 8 Stream Tutorial

2

Java 8 Nashorn Tutorial

3

Java 8 Concurrency Tutorial: Threads and Executors

4

Java 8 Concurrency Tutorial: Synchronization and Locks

5

Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap

6

Java 8 API by Example: Strings, Numbers, Math and Files

7

Avoiding Null Checks in Java 8

8

Fixing Java 8 Stream Gotchas with IntelliJ IDEA

9

Using Backbone.js with Nashorn

10

2

Modern Java - A Guide to Java 8

Modern Java - A Guide to Java 8
Author: winterbe
From: java8-tutorial
License: MIT License

Introduction

3

Modern Java - A Guide to Java 8

Modern Java - A Guide to Java 8
“Java is still not dead—and people are starting to figure that out.”
Welcome to my introduction to Java 8. This tutorial guides you step by step through all new
language features. Backed by short and simple code samples you'll learn how to use default
interface methods, lambda expressions, method references and repeatable annotations. At
the end of the article you'll be familiar with the most recent API changes like streams,
functional interfaces, map extensions and the new Date API. No walls of text, just a bunch
of commented code snippets. Enjoy!
This article was originally posted on my blog. You should follow me on Twitter.

Table of Contents
Default Methods for Interfaces
Lambda expressions
Functional Interfaces
Method and Constructor References
Lambda Scopes
Accessing local variables
Accessing fields and static variables
Accessing Default Interface Methods
Built-in Functional Interfaces
Predicates
Functions
Suppliers
Consumers
Comparators
Optionals
Streams
Filter
Sorted
Map
Match
Count
Reduce
Parallel Streams
Sequential Sort

Modern Java - A Guide to Java 8

4

Modern Java - A Guide to Java 8

Parallel Sort
Maps
Date API
Clock
Timezones
LocalTime
LocalDate
LocalDateTime
Annotations
Where to go from here?

Default Methods for Interfaces
Java 8 enables us to add non-abstract method implementations to interfaces by utilizing the
default

keyword. This feature is also known as virtual extension methods.

Here is our first example:
interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
}

Besides the abstract method
method

sqrt

calculate

the interface

Formula

also defines the default

. Concrete classes only have to implement the abstract method

The default method

sqrt

calculate

.

can be used out of the box.

Formula formula = new Formula() {
@Override
public double calculate(int a) {
return sqrt(a * 100);
}
};
formula.calculate(100);
formula.sqrt(16);

// 100.0
// 4.0

The formula is implemented as an anonymous object. The code is quite verbose: 6 lines of
code for such a simple calculation of

sqrt(a * 100)

. As we'll see in the next section, there's

a much nicer way of implementing single method objects in Java 8.

Lambda expressions

Modern Java - A Guide to Java 8

5

Modern Java - A Guide to Java 8

Let's start with a simple example of how to sort a list of strings in prior versions of Java:
List names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});

The static utility method

Collections.sort

accepts a list and a comparator in order to sort

the elements of the given list. You often find yourself creating anonymous comparators and
pass them to the sort method.
Instead of creating anonymous objects all day long, Java 8 comes with a much shorter
syntax, lambda expressions:
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});

As you can see the code is much shorter and easier to read. But it gets even shorter:
Collections.sort(names, (String a, String b) -> b.compareTo(a));

For one line method bodies you can skip both the braces

{}

and the

return

keyword. But

it gets even shorter:
names.sort((a, b) -> b.compareTo(a));

List now has a

sort

method. Also the java compiler is aware of the parameter types so you

can skip them as well. Let's dive deeper into how lambda expressions can be used in the
wild.

Functional Interfaces
How does lambda expressions fit into Java's type system? Each lambda corresponds to a
given type, specified by an interface. A so called functional interface must contain exactly
one abstract method declaration. Each lambda expression of that type will be matched to
this abstract method. Since default methods are not abstract you're free to add default
methods to your functional interface.

Modern Java - A Guide to Java 8

6

Modern Java - A Guide to Java 8

We can use arbitrary interfaces as lambda expressions as long as the interface only
contains one abstract method. To ensure that your interface meet the requirements, you
should add the

@FunctionalInterface

annotation. The compiler is aware of this annotation

and throws a compiler error as soon as you try to add a second abstract method declaration
to the interface.
Example:
@FunctionalInterface
interface Converter {
T convert(F from);
}

Converter converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted);
// 123

Keep in mind that the code is also valid if the

@FunctionalInterface

annotation would be

omitted.

Method and Constructor References
The above example code can be further simplified by utilizing static method references:
Converter converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted);
// 123

Java 8 enables you to pass references of methods or constructors via the

::

keyword. The

above example shows how to reference a static method. But we can also reference object
methods:
class Something {
String startsWith(String s) {
return String.valueOf(s.charAt(0));
}
}

Something something = new Something();
Converter converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted);
// "J"

Let's see how the

::

keyword works for constructors. First we define an example bean with

different constructors:

Modern Java - A Guide to Java 8

7

Modern Java - A Guide to Java 8

class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}

Next we specify a person factory interface to be used for creating new persons:
interface PersonFactory

{ P create(String firstName, String lastName); } Instead of implementing the factory manually, we glue everything together via constructor references: PersonFactory personFactory = Person::new; Person person = personFactory.create("Peter", "Parker"); We create a reference to the Person constructor via Person::new . The Java compiler automatically chooses the right constructor by matching the signature of PersonFactory.create . Lambda Scopes Accessing outer scope variables from lambda expressions is very similar to anonymous objects. You can access final variables from the local outer scope as well as instance fields and static variables. Accessing local variables We can read final local variables from the outer scope of lambda expressions: final int num = 1; Converter stringConverter = (from) -> String.valueOf(from + num); stringConverter.convert(2); // 3 But different to anonymous objects the variable num does not have to be declared final. This code is also valid: Modern Java - A Guide to Java 8 8 Modern Java - A Guide to Java 8 int num = 1; Converter stringConverter = (from) -> String.valueOf(from + num); stringConverter.convert(2); However num // 3 must be implicitly final for the code to compile. The following code does not compile: int num = 1; Converter stringConverter = (from) -> String.valueOf(from + num); num = 3; Writing to num from within the lambda expression is also prohibited. Accessing fields and static variables In contrast to local variables, we have both read and write access to instance fields and static variables from within lambda expressions. This behaviour is well known from anonymous objects. class Lambda4 { static int outerStaticNum; int outerNum; void testScopes() { Converter stringConverter1 = (from) -> { outerNum = 23; return String.valueOf(from); }; Converter stringConverter2 = (from) -> { outerStaticNum = 72; return String.valueOf(from); }; } } Accessing Default Interface Methods Remember the formula example from the first section? Interface method sqrt Formula defines a default which can be accessed from each formula instance including anonymous objects. This does not work with lambda expressions. Default methods cannot be accessed from within lambda expressions. The following code does not compile: Formula formula = (a) -> sqrt(a * 100); Modern Java - A Guide to Java 8 9 Modern Java - A Guide to Java 8 Built-in Functional Interfaces The JDK 1.8 API contains many built-in functional interfaces. Some of them are well known from older versions of Java like Comparator extended to enable Lambda support via the or Runnable . Those existing interfaces are @FunctionalInterface annotation. But the Java 8 API is also full of new functional interfaces to make your life easier. Some of those new interfaces are well known from the Google Guava library. Even if you're familiar with this library you should keep a close eye on how those interfaces are extended by some useful method extensions. Predicates Predicates are boolean-valued functions of one argument. The interface contains various default methods for composing predicates to complex logical terms (and, or, negate) Predicate predicate = (s) -> s.length() > 0; predicate.test("foo"); predicate.negate().test("foo"); // true // false Predicate nonNull = Objects::nonNull; Predicate isNull = Objects::isNull; Predicate isEmpty = String::isEmpty; Predicate isNotEmpty = isEmpty.negate(); Functions Functions accept one argument and produce a result. Default methods can be used to chain multiple functions together (compose, andThen). Function toInteger = Integer::valueOf; Function backToString = toInteger.andThen(String::valueOf); backToString.apply("123"); // "123" Suppliers Suppliers produce a result of a given generic type. Unlike Functions, Suppliers don't accept arguments. Supplier personSupplier = Person::new; personSupplier.get(); // new Person Consumers Modern Java - A Guide to Java 8 10 Modern Java - A Guide to Java 8 Consumers represent operations to be performed on a single input argument. Consumer greeter = (p) -> System.out.println("Hello, " + p.firstName); greeter.accept(new Person("Luke", "Skywalker")); Comparators Comparators are well known from older versions of Java. Java 8 adds various default methods to the interface. Comparator comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName); Person p1 = new Person("John", "Doe"); Person p2 = new Person("Alice", "Wonderland"); comparator.compare(p1, p2); comparator.reversed().compare(p1, p2); // > 0 // < 0 Optionals Optionals are not functional interfaces, but nifty utilities to prevent NullPointerException . It's an important concept for the next section, so let's have a quick look at how Optionals work. Optional is a simple container for a value which may be null or non-null. Think of a method which may return a non-null result but sometimes return nothing. Instead of returning you return an Optional null in Java 8. Optional optional = Optional.of("bam"); optional.isPresent(); optional.get(); optional.orElse("fallback"); // true // "bam" // "bam" optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b" Streams A java.util.Stream represents a sequence of elements on which one or more operations can be performed. Stream operations are either intermediate or terminal. While terminal operations return a result of a certain type, intermediate operations return the stream itself so you can chain multiple method calls in a row. Streams are created on a source, e.g. a java.util.Collection like lists or sets (maps are not supported). Stream operations can either be executed sequentially or parallely. Streams are extremely powerful, so I wrote a separate Java 8 Streams Tutorial. You should also check out Stream.js, a JavaScript port of the Java 8 Streams API. Modern Java - A Guide to Java 8 11 Modern Java - A Guide to Java 8 Let's first look how sequential streams work. First we create a sample source in form of a list of strings: List stringCollection = new ArrayList<>(); stringCollection.add("ddd2"); stringCollection.add("aaa2"); stringCollection.add("bbb1"); stringCollection.add("aaa1"); stringCollection.add("bbb3"); stringCollection.add("ccc"); stringCollection.add("bbb2"); stringCollection.add("ddd1"); Collections in Java 8 are extended so you can simply create streams either by calling Collection.stream() or Collection.parallelStream() . The following sections explain the most common stream operations. Filter Filter accepts a predicate to filter all elements of the stream. This operation is intermediate which enables us to call another stream operation ( forEach ) on the result. ForEach accepts a consumer to be executed for each element in the filtered stream. ForEach is a terminal operation. It's , so we cannot call another stream operation. void stringCollection .stream() .filter((s) -> s.startsWith("a")) .forEach(System.out::println); // "aaa2", "aaa1" Sorted Sorted is an intermediate operation which returns a sorted view of the stream. The elements are sorted in natural order unless you pass a custom Comparator . stringCollection .stream() .sorted() .filter((s) -> s.startsWith("a")) .forEach(System.out::println); // "aaa1", "aaa2" Keep in mind that sorted does only create a sorted view of the stream without manipulating the ordering of the backed collection. The ordering of stringCollection is untouched: System.out.println(stringCollection); // ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1 Modern Java - A Guide to Java 8 12 Modern Java - A Guide to Java 8 Map The intermediate operation map converts each element into another object via the given function. The following example converts each string into an upper-cased string. But you can also use map to transform each object into another type. The generic type of the resulting stream depends on the generic type of the function you pass to map . stringCollection .stream() .map(String::toUpperCase) .sorted((a, b) -> b.compareTo(a)) .forEach(System.out::println); // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1" Match Various matching operations can be used to check whether a certain predicate matches the stream. All of those operations are terminal and return a boolean result. boolean anyStartsWithA = stringCollection .stream() .anyMatch((s) -> s.startsWith("a")); System.out.println(anyStartsWithA); // true boolean allStartsWithA = stringCollection .stream() .allMatch((s) -> s.startsWith("a")); System.out.println(allStartsWithA); // false boolean noneStartsWithZ = stringCollection .stream() .noneMatch((s) -> s.startsWith("z")); System.out.println(noneStartsWithZ); // true Count Count is a terminal operation returning the number of elements in the stream as a long . long startsWithB = stringCollection .stream() .filter((s) -> s.startsWith("b")) .count(); System.out.println(startsWithB); // 3 Reduce Modern Java - A Guide to Java 8 13 Modern Java - A Guide to Java 8 This terminal operation performs a reduction on the elements of the stream with the given function. The result is an Optional holding the reduced value. Optional reduced = stringCollection .stream() .sorted() .reduce((s1, s2) -> s1 + "#" + s2); reduced.ifPresent(System.out::println); // "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2" Parallel Streams As mentioned above streams can be either sequential or parallel. Operations on sequential streams are performed on a single thread while operations on parallel streams are performed concurrently on multiple threads. The following example demonstrates how easy it is to increase the performance by using parallel streams. First we create a large list of unique elements: int max = 1000000; List values = new ArrayList<>(max); for (int i = 0; i < max; i++) { UUID uuid = UUID.randomUUID(); values.add(uuid.toString()); } Now we measure the time it takes to sort a stream of this collection. Sequential Sort long t0 = System.nanoTime(); long count = values.stream().sorted().count(); System.out.println(count); long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("sequential sort took: %d ms", millis)); // sequential sort took: 899 ms Parallel Sort Modern Java - A Guide to Java 8 14 Modern Java - A Guide to Java 8 long t0 = System.nanoTime(); long count = values.parallelStream().sorted().count(); System.out.println(count); long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("parallel sort took: %d ms", millis)); // parallel sort took: 472 ms As you can see both code snippets are almost identical but the parallel sort is roughly 50% faster. All you have to do is change stream() to parallelStream() . Maps As already mentioned maps do not directly support streams. There's no available on the Map stream() method interface itself, however you can create specialized streams upon the keys, values or entries of a map via map.entrySet().stream() map.keySet().stream() , map.values().stream() and . Furthermore maps support various new and useful methods for doing common tasks. Map map = new HashMap<>(); for (int i = 0; i < 10; i++) { map.putIfAbsent(i, "val" + i); } map.forEach((id, val) -> System.out.println(val)); The above code should be self-explaining: if null checks; forEach putIfAbsent prevents us from writing additional accepts a consumer to perform operations for each value of the map. This example shows how to compute code on the map by utilizing functions: map.computeIfPresent(3, (num, val) -> val + num); map.get(3); // val33 map.computeIfPresent(9, (num, val) -> null); map.containsKey(9); // false map.computeIfAbsent(23, num -> "val" + num); map.containsKey(23); // true map.computeIfAbsent(3, num -> "bam"); map.get(3); // val33 Next, we learn how to remove entries for a given key, only if it's currently mapped to a given value: Modern Java - A Guide to Java 8 15 Modern Java - A Guide to Java 8 map.remove(3, "val3"); map.get(3); // val33 map.remove(3, "val33"); map.get(3); // null Another helpful method: map.getOrDefault(42, "not found"); // not found Merging entries of a map is quite easy: map.merge(9, "val9", (value, newValue) -> value.concat(newValue)); map.get(9); // val9 map.merge(9, "concat", (value, newValue) -> value.concat(newValue)); map.get(9); // val9concat Merge either put the key/value into the map if no entry for the key exists, or the merging function will be called to change the existing value. Date API Java 8 contains a brand new date and time API under the package java.time . The new Date API is comparable with the Joda-Time library, however it's not the same. The following examples cover the most important parts of this new API. Clock Clock provides access to the current date and time. Clocks are aware of a timezone and may be used instead of System.currentTimeMillis() to retrieve the current time in milliseconds since Unix EPOCH. Such an instantaneous point on the time-line is also represented by the class Instant . Instants can be used to create legacy java.util.Date objects. Clock clock = Clock.systemDefaultZone(); long millis = clock.millis(); Instant instant = clock.instant(); Date legacyDate = Date.from(instant); // legacy java.util.Date Timezones Modern Java - A Guide to Java 8 16 Modern Java - A Guide to Java 8 Timezones are represented by a ZoneId . They can easily be accessed via static factory methods. Timezones define the offsets which are important to convert between instants and local dates and times. System.out.println(ZoneId.getAvailableZoneIds()); // prints all available timezone ids ZoneId zone1 = ZoneId.of("Europe/Berlin"); ZoneId zone2 = ZoneId.of("Brazil/East"); System.out.println(zone1.getRules()); System.out.println(zone2.getRules()); // ZoneRules[currentStandardOffset=+01:00] // ZoneRules[currentStandardOffset=-03:00] LocalTime LocalTime represents a time without a timezone, e.g. 10pm or 17:30:15. The following example creates two local times for the timezones defined above. Then we compare both times and calculate the difference in hours and minutes between both times. LocalTime now1 = LocalTime.now(zone1); LocalTime now2 = LocalTime.now(zone2); System.out.println(now1.isBefore(now2)); // false long hoursBetween = ChronoUnit.HOURS.between(now1, now2); long minutesBetween = ChronoUnit.MINUTES.between(now1, now2); System.out.println(hoursBetween); System.out.println(minutesBetween); // -3 // -239 LocalTime comes with various factory methods to simplify the creation of new instances, including parsing of time strings. LocalTime late = LocalTime.of(23, 59, 59); System.out.println(late); // 23:59:59 DateTimeFormatter germanFormatter = DateTimeFormatter .ofLocalizedTime(FormatStyle.SHORT) .withLocale(Locale.GERMAN); LocalTime leetTime = LocalTime.parse("13:37", germanFormatter); System.out.println(leetTime); // 13:37 LocalDate LocalDate represents a distinct date, e.g. 2014-03-11. It's immutable and works exactly analog to LocalTime. The sample demonstrates how to calculate new dates by adding or subtracting days, months or years. Keep in mind that each manipulation returns a new instance. Modern Java - A Guide to Java 8 17 Modern Java - A Guide to Java 8 LocalDate today = LocalDate.now(); LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS); LocalDate yesterday = tomorrow.minusDays(2); LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4); DayOfWeek dayOfWeek = independenceDay.getDayOfWeek(); System.out.println(dayOfWeek); // FRIDAY Parsing a LocalDate from a string is just as simple as parsing a LocalTime: DateTimeFormatter germanFormatter = DateTimeFormatter .ofLocalizedDate(FormatStyle.MEDIUM) .withLocale(Locale.GERMAN); LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter); System.out.println(xmas); // 2014-12-24 LocalDateTime LocalDateTime represents a date-time. It combines date and time as seen in the above sections into one instance. LocalDateTime is immutable and works similar to LocalTime and LocalDate. We can utilize methods for retrieving certain fields from a date-time: LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59); DayOfWeek dayOfWeek = sylvester.getDayOfWeek(); System.out.println(dayOfWeek); // WEDNESDAY Month month = sylvester.getMonth(); System.out.println(month); // DECEMBER long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY); System.out.println(minuteOfDay); // 1439 With the additional information of a timezone it can be converted to an instant. Instants can easily be converted to legacy dates of type java.util.Date . Instant instant = sylvester .atZone(ZoneId.systemDefault()) .toInstant(); Date legacyDate = Date.from(instant); System.out.println(legacyDate); // Wed Dec 31 23:59:59 CET 2014 Formatting date-times works just like formatting dates or times. Instead of using pre-defined formats we can create formatters from custom patterns. Modern Java - A Guide to Java 8 18 Modern Java - A Guide to Java 8 DateTimeFormatter formatter = DateTimeFormatter .ofPattern("MMM dd, yyyy - HH:mm"); LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter); String string = formatter.format(parsed); System.out.println(string); // Nov 03, 2014 - 07:13 Unlike java.text.NumberFormat the new DateTimeFormatter is immutable and thread-safe. For details on the pattern syntax read here. Annotations Annotations in Java 8 are repeatable. Let's dive directly into an example to figure that out. First, we define a wrapper annotation which holds an array of the actual annotations: @interface Hints { Hint[] value(); } @Repeatable(Hints.class) @interface Hint { String value(); } Java 8 enables us to use multiple annotations of the same type by declaring the annotation @Repeatable . Variant 1: Using the container annotation (old school) @Hints({@Hint("hint1"), @Hint("hint2")}) class Person {} Variant 2: Using repeatable annotations (new school) @Hint("hint1") @Hint("hint2") class Person {} Using variant 2 the java compiler implicitly sets up the @Hints annotation under the hood. That's important for reading annotation information via reflection. Modern Java - A Guide to Java 8 19 Modern Java - A Guide to Java 8 Hint hint = Person.class.getAnnotation(Hint.class); System.out.println(hint); // null Hints hints1 = Person.class.getAnnotation(Hints.class); System.out.println(hints1.value().length); // 2 Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class); System.out.println(hints2.length); // 2 Although we never declared the via getAnnotation(Hints.class) getAnnotationsByType @Hints annotation on the Person class, it's still readable . However, the more convenient method is which grants direct access to all annotated @Hint annotations. Furthermore the usage of annotations in Java 8 is expanded to two new targets: @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) @interface MyAnnotation {} Where to go from here? My programming guide to Java 8 ends here. If you want to learn more about all the new classes and features of the JDK 8 API, check out my JDK8 API Explorer. It helps you figuring out all the new classes and hidden gems of JDK 8, like StampedLock and CompletableFuture Arrays.parallelSort , - just to name a few. I've also published a bunch of follow-up articles on my blog that might be interesting to you: Java 8 Stream Tutorial Java 8 Nashorn Tutorial Java 8 Concurrency Tutorial: Threads and Executors Java 8 Concurrency Tutorial: Synchronization and Locks Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap Java 8 API by Example: Strings, Numbers, Math and Files Avoid Null Checks in Java 8 Fixing Java 8 Stream Gotchas with IntelliJ IDEA Using Backbone.js with Java 8 Nashorn You should follow me on Twitter. Thanks for reading! Modern Java - A Guide to Java 8 20 Modern Java - A Guide to Java 8 Java 8 Stream Tutorial July 31, 2014 This example-driven tutorial gives an in-depth overview about Java 8 streams. When I first read about the InputStream Stream and API, I was confused about the name since it sounds similar to OutputStream from Java I/O. But Java 8 streams are a completely different thing. Streams are Monads, thus playing a big part in bringing functional programming to Java: > In functional programming, a monad is a structure that represents computations defined as sequences of steps. A type with a monad structure defines what it means to chain operations, or nest functions of that type together. This guide teaches you how to work with Java 8 streams and how to use the different kind of available stream operations. You'll learn about the processing order and how the ordering of stream operations affect runtime performance. The more powerful stream operations reduce , collect and flatMap are covered in detail. The tutorial ends with an in-depth look at parallel streams. If you're not yet familiar with Java 8 lambda expressions, functional interfaces and method references, you probably want to read my Java 8 Tutorial first before starting with this tutorial. UPDATE - I'm currently working on a JavaScript implementation of the Java 8 Streams API for the browser. If I've drawn your interest check out Stream.js on GitHub. Your Feedback is highly appreciated. How streams work A stream represents a sequence of elements and supports different kind of operations to perform computations upon those elements: List myList = Arrays.asList("a1", "a2", "b1", "c2", "c1"); myList .stream() .filter(s -> s.startsWith("c")) .map(String::toUpperCase) .sorted() .forEach(System.out::println); // C1 // C2 Java 8 Stream Tutorial 21 Modern Java - A Guide to Java 8 Stream operations are either intermediate or terminal. Intermediate operations return a stream so we can chain multiple intermediate operations without using semicolons. Terminal operations are either void or return a non-stream result. In the above example map and sorted are intermediate operations whereas forEach filter , is a terminal operation. For a full list of all available stream operations see the Stream Javadoc. Such a chain of stream operations as seen in the example above is also known as operation pipeline. Most stream operations accept some kind of lambda expression parameter, a functional interface specifying the exact behavior of the operation. Most of those operations must be both non-interfering and stateless. What does that mean? A function is non-interfering when it does not modify the underlying data source of the stream, e.g. in the above example no lambda expression does modify myList by adding or removing elements from the collection. A function is stateless when the execution of the operation is deterministic, e.g. in the above example no lambda expression depends on any mutable variables or states from the outer scope which might change during execution. Different kind of streams Streams can be created from various data sources, especially collections. Lists and Sets support new methods stream() and parallelStream() to either create a sequential or a parallel stream. Parallel streams are capable of operating on multiple threads and will be covered in a later section of this tutorial. We focus on sequential streams for now: Arrays.asList("a1", "a2", "a3") .stream() .findFirst() .ifPresent(System.out::println); Calling the method // a1 on a list of objects returns a regular object stream. But we stream() don't have to create collections in order to work with streams as we see in the next code sample: Stream.of("a1", "a2", "a3") .findFirst() .ifPresent(System.out::println); Just use Stream.of() // a1 to create a stream from a bunch of object references. Besides regular object streams Java 8 ships with special kinds of streams for working with the primitive data types IntStream , LongStream Java 8 Stream Tutorial int and , long and double DoubleStream . As you might have guessed it's . 22 Modern Java - A Guide to Java 8 IntStreams can replace the regular for-loop utilizing IntStream.range() : IntStream.range(1, 4) .forEach(System.out::println); // 1 // 2 // 3 All those primitive streams work just like regular object streams with the following differences: Primitive streams use specialized lambda expressions, e.g. instead of Function or IntPredicate instead of the additional terminal aggregate operations Arrays.stream(new int[] {1, 2, 3}) .map(n -> 2 * n + 1) .average() .ifPresent(System.out::println); Predicate sum() and IntFunction . And primitive streams support average() : // 5.0 Sometimes it's useful to transform a regular object stream to a primitive stream or vice versa. For that purpose object streams support the special mapping operations mapToLong() and mapToDouble mapToInt() , : Stream.of("a1", "a2", "a3") .map(s -> s.substring(1)) .mapToInt(Integer::parseInt) .max() .ifPresent(System.out::println); // 3 Primitive streams can be transformed to object streams via mapToObj() : IntStream.range(1, 4) .mapToObj(i -> "a" + i) .forEach(System.out::println); // a1 // a2 // a3 Here's a combined example: the stream of doubles is first mapped to an int stream and than mapped to an object stream of strings: Stream.of(1.0, 2.0, 3.0) .mapToInt(Double::intValue) .mapToObj(i -> "a" + i) .forEach(System.out::println); // a1 // a2 // a3 Java 8 Stream Tutorial 23 Modern Java - A Guide to Java 8 Processing Order Now that we've learned how to create and work with different kinds of streams, let's dive deeper into how stream operations are processed under the hood. An important characteristic of intermediate operations is laziness. Look at this sample where a terminal operation is missing: Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> { System.out.println("filter: " + s); return true; }); When executing this code snippet, nothing is printed to the console. That is because intermediate operations will only be executed when a terminal operation is present. Let's extend the above example by the terminal operation forEach : Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> { System.out.println("filter: " + s); return true; }) .forEach(s -> System.out.println("forEach: " + s)); Executing this code snippet results in the desired output on the console: filter: forEach: filter: forEach: filter: forEach: filter: forEach: filter: forEach: d2 d2 a2 a2 b1 b1 b3 b3 c c The order of the result might be surprising. A naive approach would be to execute the operations horizontally one after another on all elements of the stream. But instead each element moves along the chain vertically. The first string "d2" passes forEach filter then , only then the second string "a2" is processed. This behavior can reduce the actual number of operations performed on each element, as we see in the next example: Java 8 Stream Tutorial 24 Modern Java - A Guide to Java 8 Stream.of("d2", "a2", "b1", "b3", "c") .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .anyMatch(s -> { System.out.println("anyMatch: " + s); return s.startsWith("A"); }); // // // // map: anyMatch: map: anyMatch: The operation d2 D2 a2 A2 anyMatch returns true as soon as the predicate applies to the given input element. This is true for the second element passed "A2". Due to the vertical execution of the stream chain, map has only to be executed twice in this case. So instead of mapping all elements of the stream, map will be called as few as possible. Why order matters The next example consists of two intermediate operations terminal operation forEach map and filter and the . Let's once again inspect how those operations are being executed: Stream.of("d2", "a2", "b1", "b3", "c") .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .filter(s -> { System.out.println("filter: " + s); return s.startsWith("A"); }) .forEach(s -> System.out.println("forEach: " + s)); // // // // // // // // // // // map: filter: map: filter: forEach: map: filter: map: filter: map: filter: d2 D2 a2 A2 A2 b1 B1 b3 B3 c C As you might have guessed both the underlying collection whereas map and forEach filter are called five times for every string in is only called once. We can greatly reduce the actual number of executions if we change the order of the operations, moving filter Java 8 Stream Tutorial to the beginning of the chain: 25 Modern Java - A Guide to Java 8 Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> { System.out.println("filter: " + s); return s.startsWith("a"); }) .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .forEach(s -> System.out.println("forEach: " + s)); // // // // // // // Now, filter: filter: map: forEach: filter: filter: filter: map d2 a2 a2 A2 b1 b3 c is only called once so the operation pipeline performs much faster for larger numbers of input elements. Keep that in mind when composing complex method chains. Let's extend the above example by an additional operation, sorted : Stream.of("d2", "a2", "b1", "b3", "c") .sorted((s1, s2) -> { System.out.printf("sort: %s; %s\n", s1, s2); return s1.compareTo(s2); }) .filter(s -> { System.out.println("filter: " + s); return s.startsWith("a"); }) .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .forEach(s -> System.out.println("forEach: " + s)); Sorting is a special kind of intermediate operation. It's a so called stateful operation since in order to sort a collection of elements you have to maintain state during ordering. Executing this example results in the following console output: sort: sort: sort: sort: sort: sort: sort: sort: filter: map: forEach: filter: filter: filter: filter: a2; d2 b1; a2 b1; d2 b1; a2 b3; b1 b3; d2 c; b3 c; d2 a2 a2 A2 b1 b3 c d2 Java 8 Stream Tutorial 26 Modern Java - A Guide to Java 8 First, the sort operation is executed on the entire input collection. In other words executed horizontally. So in this case sorted sorted is is called eight times for multiple combinations on every element in the input collection. Once again we can optimize the performance by reordering the chain: Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> { System.out.println("filter: " + s); return s.startsWith("a"); }) .sorted((s1, s2) -> { System.out.printf("sort: %s; %s\n", s1, s2); return s1.compareTo(s2); }) .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .forEach(s -> System.out.println("forEach: " + s)); // // // // // // // filter: filter: filter: filter: filter: map: forEach: d2 a2 b1 b3 c a2 A2 In this example sorted is never been called because filter reduces the input collection to just one element. So the performance is greatly increased for larger input collections. Reusing Streams Java 8 streams cannot be reused. As soon as you call any terminal operation the stream is closed: Stream stream = Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> s.startsWith("a")); stream.anyMatch(s -> true); stream.noneMatch(s -> true); Calling noneMatch after anyMatch // ok // exception on the same stream results in the following exception: java.lang.IllegalStateException: stream has already been operated upon or closed at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229) at java.util.stream.ReferencePipeline.noneMatch(ReferencePipeline.java:459) at com.winterbe.java8.Streams5.test7(Streams5.java:38) at com.winterbe.java8.Streams5.main(Streams5.java:28) Java 8 Stream Tutorial 27 Modern Java - A Guide to Java 8 To overcome this limitation we have to to create a new stream chain for every terminal operation we want to execute, e.g. we could create a stream supplier to construct a new stream with all intermediate operations already set up: Supplier> streamSupplier = () -> Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> s.startsWith("a")); streamSupplier.get().anyMatch(s -> true); streamSupplier.get().noneMatch(s -> true); Each call to get() // ok // ok constructs a new stream on which we are save to call the desired terminal operation. Advanced Operations Streams support plenty of different operations. We've already learned about the most important operations like filter or map . I leave it up to you to discover all other available operations (see Stream Javadoc). Instead let's dive deeper into the more complex operations collect , flatMap and reduce . Most code samples from this section use the following list of persons for demonstration purposes: class Person { String name; int age; Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return name; } } List persons = Arrays.asList( new Person("Max", 18), new Person("Peter", 23), new Person("Pamela", 23), new Person("David", 12)); Collect Collect is an extremely useful terminal operation to transform the elements of the stream into a different kind of result, e.g. a List , Set or Map . Collect accepts a Collector which consists of four different operations: a supplier, an accumulator, a combiner and a finisher. Java 8 Stream Tutorial 28 Modern Java - A Guide to Java 8 This sounds super complicated at first, but the good part is Java 8 supports various built-in collectors via the Collectors class. So for the most common operations you don't have to implement a collector yourself. Let's start with a very common usecase: List filtered = persons .stream() .filter(p -> p.name.startsWith("P")) .collect(Collectors.toList()); System.out.println(filtered); // [Peter, Pamela] As you can see it's very simple to construct a list from the elements of a stream. Need a set instead of list - just use Collectors.toSet() . The next example groups all persons by age: Map> personsByAge = persons .stream() .collect(Collectors.groupingBy(p -> p.age)); personsByAge .forEach((age, p) -> System.out.format("age %s: %s\n", age, p)); // age 18: [Max] // age 23: [Peter, Pamela] // age 12: [David] Collectors are extremely versatile. You can also create aggregations on the elements of the stream, e.g. determining the average age of all persons: Double averageAge = persons .stream() .collect(Collectors.averagingInt(p -> p.age)); System.out.println(averageAge); // 19.0 If you're interested in more comprehensive statistics, the summarizing collectors return a special built-in summary statistics object. So we can simply determine min, max and arithmetic average age of the persons as well as the sum and count. IntSummaryStatistics ageSummary = persons .stream() .collect(Collectors.summarizingInt(p -> p.age)); System.out.println(ageSummary); // IntSummaryStatistics{count=4, sum=76, min=12, average=19.000000, max=23} The next example joins all persons into a single string: Java 8 Stream Tutorial 29 Modern Java - A Guide to Java 8 String phrase = persons .stream() .filter(p -> p.age >= 18) .map(p -> p.name) .collect(Collectors.joining(" and ", "In Germany ", " are of legal age.")); System.out.println(phrase); // In Germany Max and Peter and Pamela are of legal age. The join collector accepts a delimiter as well as an optional prefix and suffix. In order to transform the stream elements into a map, we have to specify how both the keys and the values should be mapped. Keep in mind that the mapped keys must be unique, otherwise an IllegalStateException is thrown. You can optionally pass a merge function as an additional parameter to bypass the exception: Map map = persons .stream() .collect(Collectors.toMap( p -> p.age, p -> p.name, (name1, name2) -> name1 + ";" + name2)); System.out.println(map); // {18=Max, 23=Peter;Pamela, 12=David} Now that we know some of the most powerful built-in collectors, let's try to build our own special collector. We want to transform all persons of the stream into a single string consisting of all names in upper letters separated by the achieve this we create a new collector via Collector.of() | pipe character. In order to . We have to pass the four ingredients of a collector: a supplier, an accumulator, a combiner and a finisher. Collector personNameCollector = Collector.of( () -> new StringJoiner(" | "), // supplier (j, p) -> j.add(p.name.toUpperCase()), // accumulator (j1, j2) -> j1.merge(j2), // combiner StringJoiner::toString); // finisher String names = persons .stream() .collect(personNameCollector); System.out.println(names); // MAX | PETER | PAMELA | DAVID Since strings in Java are immutable, we need a helper class like StringJoiner to let the collector construct our string. The supplier initially constructs such a StringJoiner with the appropriate delimiter. The accumulator is used to add each persons upper-cased name to the StringJoiner. The combiner knows how to merge two StringJoiners into one. In the last step the finisher constructs the desired String from the StringJoiner. FlatMap Java 8 Stream Tutorial 30 Modern Java - A Guide to Java 8 We've already learned how to transform the objects of a stream into another type of objects by utilizing the map operation. Map is kinda limited because every object can only be mapped to exactly one other object. But what if we want to transform one object into multiple others or none at all? This is where flatMap comes to the rescue. FlatMap transforms each element of the stream into a stream of other objects. So each object will be transformed into zero, one or multiple other objects backed by streams. The contents of those streams will then be placed into the returned stream of the flatMap operation. Before we see flatMap in action we need an appropriate type hierarchy: class Foo { String name; List bars = new ArrayList<>(); Foo(String name) { this.name = name; } } class Bar { String name; Bar(String name) { this.name = name; } } Next, we utilize our knowledge about streams to instantiate a couple of objects: List foos = new ArrayList<>(); // create foos IntStream .range(1, 4) .forEach(i -> foos.add(new Foo("Foo" + i))); // create bars foos.forEach(f -> IntStream .range(1, 4) .forEach(i -> f.bars.add(new Bar("Bar" + i + " <- " + f.name)))); Now we have a list of three foos each consisting of three bars. FlatMap accepts a function which has to return a stream of objects. So in order to resolve the bar objects of each foo, we just pass the appropriate function: Java 8 Stream Tutorial 31 Modern Java - A Guide to Java 8 foos.stream() .flatMap(f -> f.bars.stream()) .forEach(b -> System.out.println(b.name)); // // // // // // // // // Bar1 Bar2 Bar3 Bar1 Bar2 Bar3 Bar1 Bar2 Bar3 <<<<<<<<<- Foo1 Foo1 Foo1 Foo2 Foo2 Foo2 Foo3 Foo3 Foo3 As you can see, we've successfully transformed the stream of three foo objects into a stream of nine bar objects. Finally, the above code example can be simplified into a single pipeline of stream operations: IntStream.range(1, 4) .mapToObj(i -> new Foo("Foo" + i)) .peek(f -> IntStream.range(1, 4) .mapToObj(i -> new Bar("Bar" + i + " <- " f.name)) .forEach(f.bars::add)) .flatMap(f -> f.bars.stream()) .forEach(b -> System.out.println(b.name)); FlatMap is also available for the Optional class introduced in Java 8. Optionals flatMap operation returns an optional object of another type. So it can be utilized to prevent nasty null checks. Think of a highly hierarchical structure like this: class Outer { Nested nested; } class Nested { Inner inner; } class Inner { String foo; } In order to resolve the inner string foo of an outer instance you have to add multiple null checks to prevent possible NullPointerExceptions: Outer outer = new Outer(); if (outer != null && outer.nested != null && outer.nested.inner != null) { System.out.println(outer.nested.inner.foo); } The same behavior can be obtained by utilizing optionals Java 8 Stream Tutorial flatMap operation: 32 Modern Java - A Guide to Java 8 Optional.of(new Outer()) .flatMap(o -> Optional.ofNullable(o.nested)) .flatMap(n -> Optional.ofNullable(n.inner)) .flatMap(i -> Optional.ofNullable(i.foo)) .ifPresent(System.out::println); Each call to returns an flatMap Optional wrapping the desired object if present or null if absent. Reduce The reduction operation combines all elements of the stream into a single result. Java 8 supports three different kind of reduce methods. The first one reduces a stream of elements to exactly one element of the stream. Let's see how we can use this method to determine the oldest person: persons .stream() .reduce((p1, p2) -> p1.age > p2.age ? p1 : p2) .ifPresent(System.out::println); // Pamela The reduce BiFunction are like method accepts a BinaryOperator accumulator function. That's actually a where both operands share the same type, in that case Function Person . BiFunctions but accept two arguments. The example function compares both persons ages in order to return the person with the maximum age. The second reduce method accepts both an identity value and a BinaryOperator accumulator. This method can be utilized to construct a new Person with the aggregated names and ages from all other persons in the stream: Person result = persons .stream() .reduce(new Person("", 0), (p1, p2) -> { p1.age += p2.age; p1.name += p2.name; return p1; }); System.out.format("name=%s; age=%s", result.name, result.age); // name=MaxPeterPamelaDavid; age=76 The third reduce method accepts three parameters: an identity value, a accumulator and a combiner function of type is not restricted to the Person BinaryOperator BiFunction . Since the identity values type type, we can utilize this reduction to determine the sum of ages from all persons: Java 8 Stream Tutorial 33 Modern Java - A Guide to Java 8 Integer ageSum = persons .stream() .reduce(0, (sum, p) -> sum += p.age, (sum1, sum2) -> sum1 + sum2); System.out.println(ageSum); // 76 As you can see the result is 76, but what's happening exactly under the hood? Let's extend the above code by some debug output: Integer ageSum = persons .stream() .reduce(0, (sum, p) -> { System.out.format("accumulator: sum=%s; person=%s\n", sum, p); return sum += p.age; }, (sum1, sum2) -> { System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2); return sum1 + sum2; }); // // // // accumulator: accumulator: accumulator: accumulator: sum=0; person=Max sum=18; person=Peter sum=41; person=Pamela sum=64; person=David As you can see the accumulator function does all the work. It first get called with the initial identity value 0 and the first person Max. In the next three steps sum continually increases by the age of the last steps person up to a total age of 76. Wait wat? The combiner never gets called? Executing the same stream in parallel will lift the secret: Integer ageSum = persons .parallelStream() .reduce(0, (sum, p) -> { System.out.format("accumulator: sum=%s; person=%s\n", sum, p); return sum += p.age; }, (sum1, sum2) -> { System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2); return sum1 + sum2; }); // // // // // // // accumulator: sum=0; person=Pamela accumulator: sum=0; person=David accumulator: sum=0; person=Max accumulator: sum=0; person=Peter combiner: sum1=18; sum2=23 combiner: sum1=23; sum2=12 combiner: sum1=41; sum2=35 Executing this stream in parallel results in an entirely different execution behavior. Now the combiner is actually called. Since the accumulator is called in parallel, the combiner is needed to sum up the separate accumulated values. Java 8 Stream Tutorial 34 Modern Java - A Guide to Java 8 Let's dive deeper into parallel streams in the next chapter. Parallel Streams Streams can be executed in parallel to increase runtime performance on large amount of input elements. Parallel streams use a common ForkJoinPool.commonPool() ForkJoinPool available via the static method. The size of the underlying thread-pool uses up to five threads - depending on the amount of available physical CPU cores: ForkJoinPool commonPool = ForkJoinPool.commonPool(); System.out.println(commonPool.getParallelism()); // 3 On my machine the common pool is initialized with a parallelism of 3 per default. This value can be decreased or increased by setting the following JVM parameter: -Djava.util.concurrent.ForkJoinPool.common.parallelism=5 Collections support the method parallelStream() Alternatively you can call the intermediate method to create a parallel stream of elements. parallel() on a given stream to convert a sequential stream to a parallel counterpart. In order to understate the parallel execution behavior of a parallel stream the next example prints information about the current thread to sout : Arrays.asList("a1", "a2", "b1", "c2", "c1") .parallelStream() .filter(s -> { System.out.format("filter: %s [%s]\n", s, Thread.currentThread().getName()); return true; }) .map(s -> { System.out.format("map: %s [%s]\n", s, Thread.currentThread().getName()); return s.toUpperCase(); }) .forEach(s -> System.out.format("forEach: %s [%s]\n", s, Thread.currentThread().getName())); By investigating the debug output we should get a better understanding which threads are actually used to execute the stream operations: Java 8 Stream Tutorial 35 Modern Java - A Guide to Java 8 filter: filter: map: filter: map: filter: map: forEach: forEach: map: forEach: filter: map: forEach: forEach: b1 a2 a2 c2 c2 c1 c1 C2 A2 b1 B1 a1 a1 A1 C1 [main] [ForkJoinPool.commonPool-worker-1] [ForkJoinPool.commonPool-worker-1] [ForkJoinPool.commonPool-worker-3] [ForkJoinPool.commonPool-worker-3] [ForkJoinPool.commonPool-worker-2] [ForkJoinPool.commonPool-worker-2] [ForkJoinPool.commonPool-worker-3] [ForkJoinPool.commonPool-worker-1] [main] [main] [ForkJoinPool.commonPool-worker-3] [ForkJoinPool.commonPool-worker-3] [ForkJoinPool.commonPool-worker-3] [ForkJoinPool.commonPool-worker-2] As you can see the parallel stream utilizes all available threads from the common ForkJoinPool for executing the stream operations. The output may differ in consecutive runs because the behavior which particular thread is actually used is non-deterministic. Let's extend the example by an additional stream operation, sort : Arrays.asList("a1", "a2", "b1", "c2", "c1") .parallelStream() .filter(s -> { System.out.format("filter: %s [%s]\n", s, Thread.currentThread().getName()); return true; }) .map(s -> { System.out.format("map: %s [%s]\n", s, Thread.currentThread().getName()); return s.toUpperCase(); }) .sorted((s1, s2) -> { System.out.format("sort: %s <> %s [%s]\n", s1, s2, Thread.currentThread().getName()); return s1.compareTo(s2); }) .forEach(s -> System.out.format("forEach: %s [%s]\n", s, Thread.currentThread().getName())); The result may look strange at first: Java 8 Stream Tutorial 36 Modern Java - A Guide to Java 8 filter: filter: map: filter: map: filter: map: filter: map: map: sort: sort: sort: sort: sort: sort: forEach: forEach: forEach: forEach: forEach: c2 c1 c1 a2 a2 b1 b1 a1 a1 c2 A2 B1 C2 C1 C1 C1 A1 C2 B1 A2 C1 It seems that [ForkJoinPool.commonPool-worker-3] [ForkJoinPool.commonPool-worker-2] [ForkJoinPool.commonPool-worker-2] [ForkJoinPool.commonPool-worker-1] [ForkJoinPool.commonPool-worker-1] [main] [main] [ForkJoinPool.commonPool-worker-2] [ForkJoinPool.commonPool-worker-2] [ForkJoinPool.commonPool-worker-3] <> A1 [main] <> A2 [main] <> B1 [main] <> C2 [main] <> B1 [main] <> C2 [main] [ForkJoinPool.commonPool-worker-1] [ForkJoinPool.commonPool-worker-3] [main] [ForkJoinPool.commonPool-worker-2] [ForkJoinPool.commonPool-worker-1] sort is executed sequentially on the main thread only. Actually, parallel stream uses the new Java 8 method Arrays.parallelSort() sort on a under the hood. As stated in Javadoc this method decides on the length of the array if sorting will be performed sequentially or in parallel: > If the length of the specified array is less than the minimum granularity, then it is sorted using the appropriate Arrays.sort method. Coming back to the reduce example from the last section. We already found out that the combiner function is only called in parallel but not in sequential streams. Let's see which threads are actually involved: List persons = Arrays.asList( new Person("Max", 18), new Person("Peter", 23), new Person("Pamela", 23), new Person("David", 12)); persons .parallelStream() .reduce(0, (sum, p) -> { System.out.format("accumulator: sum=%s; person=%s [%s]\n", sum, p, Thread.currentThread().getName()); return sum += p.age; }, (sum1, sum2) -> { System.out.format("combiner: sum1=%s; sum2=%s [%s]\n", sum1, sum2, Thread.currentThread().getName()); return sum1 + sum2; }); The console output reveals that both the accumulator and the combiner functions are executed in parallel on all available threads: Java 8 Stream Tutorial 37 Modern Java - A Guide to Java 8 accumulator: accumulator: accumulator: accumulator: combiner: combiner: combiner: sum=0; person=Pamela; sum=0; person=Max; sum=0; person=David; sum=0; person=Peter; sum1=18; sum2=23; sum1=23; sum2=12; sum1=41; sum2=35; [main] [ForkJoinPool.commonPool-worker-3] [ForkJoinPool.commonPool-worker-2] [ForkJoinPool.commonPool-worker-1] [ForkJoinPool.commonPool-worker-1] [ForkJoinPool.commonPool-worker-2] [ForkJoinPool.commonPool-worker-2] In summary, it can be stated that parallel streams can bring be a nice performance boost to streams with a large amount of input elements. But keep in mind that some parallel stream operations like reduce and collect need additional computations (combine operations) which isn't needed when executed sequentially. Furthermore we've learned that all parallel stream operations share the same JVM-wide common ForkJoinPool . So you probably want to avoid implementing slow blocking stream operations since that could potentially slow down other parts of your application which rely heavily on parallel streams. That's it My programming guide to Java 8 streams ends here. If you're interested in learning more about Java 8 streams, I recommend to you the Stream Javadoc package documentation. If you want to learn more about the underlying mechanisms, you probably want to read Martin Fowlers article about Collection Pipelines. If you're interested in JavaScript as well, you may want to have a look at Stream.js - a JavaScript implementation of the Java 8 Streams API. You may also wanna read my Java 8 Tutorial and my Java 8 Nashorn Tutorial. Hopefully this tutorial was helpful to you and you've enjoyed reading it. The full source code of the tutorial samples is hosted on GitHub. Feel free to fork the repository or send me your feedback via Twitter. Happy coding! Java 8 Stream Tutorial 38 Modern Java - A Guide to Java 8 Java 8 Nashorn Tutorial April 05, 2014 Learn all about the Nashorn Javascript Engine with easily understood code examples. The Nashorn Javascript Engine is part of Java SE 8 and competes with other standalone engines like Google V8 (the engine that powers Google Chrome and Node.js). Nashorn extends Javas capabilities by running dynamic javascript code natively on the JVM. In the next ~15 minutes you learn how to evaluate javascript on the JVM dynamically during runtime. The most recent Nashorn language features are demonstrated with small code examples. You learn how to call javascript functions from java code and vice versa. At the end you're ready to integrate dynamic scripts in your daily java business. UPDATE - I'm currently working on a JavaScript implementation of the Java 8 Streams API for the browser. If I've drawn your interest check out Stream.js on GitHub. Your Feedback is highly appreciated. Using Nashorn The Nashorn javascript engine can either be used programmatically from java programs or by utilizing the command line tool work with jjs jjs , which is located in $JAVA_HOME/bin . If you plan to you might want to put a symbolic link for simple access: Java 8 Nashorn Tutorial 39 Modern Java - A Guide to Java 8 $ cd /usr/bin $ ln -s $JAVA_HOME/bin/jjs jjs $ jjs jjs> print('Hello World'); This tutorial focuses on using nashorn from java code, so let's skip jjs for now. A simple HelloWorld in java code looks like this: ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval("print('Hello World!');"); In order to evaluate javascript code from java, you first create a nashorn script engine by utilizing the javax.script package already known from Rhino (Javas legacy js engine from Mozilla). Javascript code can either be evaluated directly by passing javascript code as a string as shown above. Or you can pass a file reader pointing to your .js script file: ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval(new FileReader("script.js")); Nashorn javascript is based on ECMAScript 5.1 but future versions of nashorn will include support for ECMAScript 6: > The current strategy for Nashorn is to follow the ECMAScript specification. When we release with JDK 8 we will be aligned with ECMAScript 5.1. The follow up major release of Nashorn will align with ECMAScript Edition 6. Nashorn defines a lot of language and API extensions to the ECMAScript standard. But first let's take a look at how the communication between java and javascript code works. Invoking Javascript Functions from Java Nashorn supports the invocation of javascript functions defined in your script files directly from java code. You can pass java objects as function arguments and return data back from the function to the calling java method. The following javascript functions will later be called from the java side: var fun1 = function(name) { print('Hi there from Javascript, ' + name); return "greetings from javascript"; }; var fun2 = function (object) { print("JS Class Definition: " + Object.prototype.toString.call(object)); }; Java 8 Nashorn Tutorial 40 Modern Java - A Guide to Java 8 In order to call a function you first have to cast the script engine to Invocable interface is implemented by the a method invokeFunction NashornScriptEngine Invocable . The implementation and defines to call a javascript function for a given name. ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval(new FileReader("script.js")); Invocable invocable = (Invocable) engine; Object result = invocable.invokeFunction("fun1", "Peter Parker"); System.out.println(result); System.out.println(result.getClass()); // Hi there from Javascript, Peter Parker // greetings from javascript // class java.lang.String Executing the code results in three lines written to the console. Calling the function pipes the result to System.out print , so we see the javascript message first. Now let's call the second function by passing arbitrary java objects: invocable.invokeFunction("fun2", new Date()); // [object java.util.Date] invocable.invokeFunction("fun2", LocalDateTime.now()); // [object java.time.LocalDateTime] invocable.invokeFunction("fun2", new Person()); // [object com.winterbe.java8.Person] Java objects can be passed without loosing any type information on the javascript side. Since the script runs natively on the JVM we can utilize the full power of the Java API or external libraries on nashorn. Invoking Java Methods from Javascript Invoking java methods from javascript is quite easy. We first define a static java method: static String fun1(String name) { System.out.format("Hi there from Java, %s", name); return "greetings from java"; } Java classes can be referenced from javascript via the Java.type API extension. It's similar to importing classes in java code. As soon as the java type is defined we naturally call the static method fun1() and print the result to sout . Since the method is static, we don't have to create an instance first. Java 8 Nashorn Tutorial 41 Modern Java - A Guide to Java 8 var MyJavaClass = Java.type('my.package.MyJavaClass'); var result = MyJavaClass.fun1('John Doe'); print(result); // Hi there from Java, John Doe // greetings from java How does Nashorn handle type conversion when calling java methods with native javascript types? Let's find out with a simple example. The following java method simply prints the actual class type of the method parameter: static void fun2(Object object) { System.out.println(object.getClass()); } To understand how type conversations are handled under the hood, we call this method with different javascript types: MyJavaClass.fun2(123); // class java.lang.Integer MyJavaClass.fun2(49.99); // class java.lang.Double MyJavaClass.fun2(true); // class java.lang.Boolean MyJavaClass.fun2("hi there") // class java.lang.String MyJavaClass.fun2(new Number(23)); // class jdk.nashorn.internal.objects.NativeNumber MyJavaClass.fun2(new Date()); // class jdk.nashorn.internal.objects.NativeDate MyJavaClass.fun2(new RegExp()); // class jdk.nashorn.internal.objects.NativeRegExp MyJavaClass.fun2({foo: 'bar'}); // class jdk.nashorn.internal.scripts.JO4 Primitive javascript types are converted to the appropriate java wrapper class. Instead native javascript objects are represented by internal adapter classes. Please keep in mind that classes from jdk.nashorn.internal are subject to change, so you shouldn't program against those classes in client-code: > Anything marked internal will likely change out from underneath you. ScriptObjectMirror Java 8 Nashorn Tutorial 42 Modern Java - A Guide to Java 8 When passing native javascript objects to java you can utilize the class ScriptObjectMirror which is actually a java representation of the underlying javascript object. ScriptObjectMirror implements the map interface and resides inside the package . Classes jdk.nashorn.api from this package are intended to be used in client-code. The next sample changes the parameter type from Object to ScriptObjectMirror so we can extract some infos from the passed javascript object: static void fun3(ScriptObjectMirror mirror) { System.out.println(mirror.getClassName() + ": " + Arrays.toString(mirror.getOwnKeys(true))); } When passing an object hash to this method, the properties are accessible on the java side: MyJavaClass.fun3({ foo: 'bar', bar: 'foo' }); // Object: [foo, bar] We can also call member functions on javascript object from java. Let's first define a javascript type Person with properties firstName and lastName and method . getFullName function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this.getFullName = function() { return this.firstName + " " + this.lastName; } } The javascript method callMember() getFullName can be called on the ScriptObjectMirror via . static void fun4(ScriptObjectMirror person) { System.out.println("Full Name is: " + person.callMember("getFullName")); } When passing a new person to the java method, we see the desired result on the console: var person1 = new Person("Peter", "Parker"); MyJavaClass.fun4(person1); // Full Name is: Peter Parker Language Extensions Java 8 Nashorn Tutorial 43 Modern Java - A Guide to Java 8 Nashorn defines various language and API extensions to the ECMAScript standard. Let's head right into the most recent features: Typed Arrays Native javascript arrays are untyped. Nashorn enables you to use typed java arrays in javascript: var IntArray = Java.type("int[]"); var array = new IntArray(5); array[0] = 5; array[1] = 4; array[2] = 3; array[3] = 2; array[4] = 1; try { array[5] = 23; } catch (e) { print(e.message); } array[0] = "17"; print(array[0]); // Array index out of range: 5 // 17 array[0] = "wrong type"; print(array[0]); // 0 array[0] = "17.3"; print(array[0]); // 17 The int[] array behaves like a real java int array. But additionally Nashorn performs implicit type conversions under the hood when we're trying to add non-integer values to the array. Strings will be auto-converted to int which is quite handy. Collections and For Each Instead of messing around with arrays we can use any java collection. First define the java type via Java.type , then create new instances on demand. var ArrayList = Java.type('java.util.ArrayList'); var list = new ArrayList(); list.add('a'); list.add('b'); list.add('c'); for each (var el in list) print(el); // a, b, c In order to iterate over collections and arrays Nashorn introduces the for each statement. It works just like the foreach loop in java. Here's another collection foreach example, utilizing Java 8 Nashorn Tutorial HashMap : 44 Modern Java - A Guide to Java 8 var map = new java.util.HashMap(); map.put('foo', 'val1'); map.put('bar', 'val2'); for each (var e in map.keySet()) print(e); // foo, bar for each (var e in map.values()) print(e); // val1, val2 Lambda expressions and Streams Everyone loves lambdas and streams - so does Nashorn! Although ECMAScript 5.1 lacks the compact arrow syntax from the Java 8 lambda expressions, we can use function literals where ever lambda expressions are accepted. var list2 = new java.util.ArrayList(); list2.add("ddd2"); list2.add("aaa2"); list2.add("bbb1"); list2.add("aaa1"); list2.add("bbb3"); list2.add("ccc"); list2.add("bbb2"); list2.add("ddd1"); list2 .stream() .filter(function(el) { return el.startsWith("aaa"); }) .sorted() .forEach(function(el) { print(el); }); // aaa1, aaa2 Extending classes Java types can simply be extended with the Java.extend extension. As you can see in the next example, you can even create multi-threaded code in your scripts: var Runnable = Java.type('java.lang.Runnable'); var Printer = Java.extend(Runnable, { run: function() { print('printed from a separate thread'); } }); var Thread = Java.type('java.lang.Thread'); new Thread(new Printer()).start(); new Thread(function() { print('printed from another thread'); }).start(); // printed from a separate thread // printed from another thread Parameter overloading Java 8 Nashorn Tutorial 45 Modern Java - A Guide to Java 8 Methods and functions can either be called with the point notation or with the square braces notation. var System = Java.type('java.lang.System'); System.out.println(10); // 10 System.out["println"](11.0); // 11.0 System.out["println(double)"](12); // 12.0 Passing the optional parameter type println(double) when calling a method with overloaded parameters determines the exact method to be called. Java Beans Instead of explicitly working with getters and setters you can just use simple property names both for getting or setting values from a java bean. var Date = Java.type('java.util.Date'); var date = new Date(); date.year += 1900; print(date.year); // 2014 Function Literals For simple one line functions we can skip the curly braces: function sqr(x) x * x; print(sqr(3)); // 9 Binding properties Properties from two different objects can be bound together: var o1 = {}; var o2 = { foo: 'bar'}; Object.bindProperties(o1, o2); print(o1.foo); o1.foo = 'BAM'; print(o2.foo); // bar // BAM Trimming strings I like my strings trimmed. print(" hehe".trimLeft()); print("hehe ".trimRight() + "he"); // hehe // hehehe Whereis Java 8 Nashorn Tutorial 46 Modern Java - A Guide to Java 8 In case you forget where you are: print(__FILE__, __LINE__, __DIR__); Import Scopes Sometimes it's useful to import many java packages at once. We can use the class JavaImporter to be used in conjunction with the with statement. All class files from the imported packages are accessible within the local scope of the with statement: var imports = new JavaImporter(java.io, java.lang); with (imports) { var file = new File(__FILE__); System.out.println(file.getAbsolutePath()); // /path/to/my/script.js } Convert arrays Some packages like JavaImporter java.util can be accessed directly without utilizing Java.type or : var list = new java.util.ArrayList(); list.add("s1"); list.add("s2"); list.add("s3"); This code converts the java list to a native javascript array: var jsArray = Java.from(list); print(jsArray); print(Object.prototype.toString.call(jsArray)); // s1,s2,s3 // [object Array] And the other way around: var javaArray = Java.to([3, 5, 7, 11], "int[]"); Calling Super Accessing overridden members in javascript is traditionally awkward because javas super keyword doesn't exist in ECMAScript. Luckily nashorn goes to the rescue. First we define a super type in java code: Java 8 Nashorn Tutorial 47 Modern Java - A Guide to Java 8 class SuperRunner implements Runnable { @Override public void run() { System.out.println("super run"); } } Next we override from javascript. Pay attention to the extended nashorn SuperRunner syntax when creating a new Runner instance: The syntax of overriding members is borrowed from javas anonymous objects. var SuperRunner = Java.type('com.winterbe.java8.SuperRunner'); var Runner = Java.extend(SuperRunner); var runner = new Runner() { run: function() { Java.super(runner).run(); print('on my run'); } } runner.run(); // super run // on my run We call the overridden method SuperRunner.run() by utilizing the Java.super extension. Loading scripts Evaluating additional script files from javascript is quite easy. We can load both local or remote scripts with the load function. I'm using Underscore.js a lot for my web front-ends, so let's reuse Underscore in Nashorn: load('http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js'); var odds = _.filter([1, 2, 3, 4, 5, 6], function (num) { return num % 2 == 1; }); print(odds); // 1, 3, 5 The external script will be evaluated in the same javascript context, so we can access the underscore variable directly. Keep in mind that loading scripts can potentially break your own code when variable names are overlapping each other. This problem can be bypassed by loading script files into a new global context: loadWithNewGlobal('script.js'); Command-line scripts Java 8 Nashorn Tutorial 48 Modern Java - A Guide to Java 8 If you're interested in writing command-line (shell) scripts with Java, give Nake a try. Nake is a simplified Make for Java 8 Nashorn. You define tasks in a project-specific run those tasks by typing nake -- myTask Nakefile , then into the command line. Tasks are written in javascript and run in Nashorns scripting mode, so you can utilize the full power of your terminal as well as the JDK8 API and any java library. For Java Developers writing command-line scripts is easy as never before... That's it I hope this guide was helpful to you and you enjoyed our journey to the Nashorn Javascript Engine. For further information about Nashorn read here, here and here. A guide to coding shell scripts with Nashorn can be found here. I recently published a follow up article about how to use Backbone.js models with the Nashorn Javascript Engine. If you want to learn more about Java 8 feel free to read my Java 8 Tutorial and my Java 8 Stream Tutorial. The runnable source code from this Nashorn tutorial is hosted on GitHub. Feel free to fork the repository or send me your feedback via Twitter. Keep on coding! Java 8 Nashorn Tutorial 49 Modern Java - A Guide to Java 8 Java 8 Concurrency Tutorial: Threads and Executors April 07, 2015 Welcome to the first part of my Java 8 Concurrency tutorial. This guide teaches you concurrent programming in Java 8 with easily understood code examples. It's the first part out of a series of tutorials covering the Java Concurrency API. In the next 15 min you learn how to execute code in parallel via threads, tasks and executor services. Part 1: Threads and Executors Part 2: Synchronization and Locks Part 3: Atomic Variables and ConcurrentMap The Concurrency API was first introduced with the release of Java 5 and then progressively enhanced with every new Java release. The majority of concepts shown in this article also work in older versions of Java. However my code samples focus on Java 8 and make heavy use of lambda expressions and other new features. If you're not yet familiar with lambdas I recommend reading my Java 8 Tutorial first. Threads and Runnables All modern operating systems support concurrency both via processes) and threads. Processes are instances of programs which typically run independent to each other, e.g. if you start a java program the operating system spawns a new process which runs in parallel to other programs. Inside those processes we can utilize threads to execute code concurrently, so we can make the most out of the available cores of the CPU. Java supports Threads since JDK 1.0. Before starting a new thread you have to specify the code to be executed by this thread, often called the task. This is done by implementing Runnable - a functional interface defining a single void no-args method run() as demonstrated in the following example: Runnable task = () -> { String threadName = Thread.currentThread().getName(); System.out.println("Hello " + threadName); }; task.run(); Thread thread = new Thread(task); thread.start(); System.out.println("Done!"); Java 8 Concurrency Tutorial: Threads and Executors 50 Modern Java - A Guide to Java 8 Since Runnable is a functional interface we can utilize Java 8 lambda expressions to print the current threads name to the console. First we execute the runnable directly on the main thread before starting a new thread. The result on the console might look like this: Hello main Hello Thread-0 Done! Or that: Hello main Done! Hello Thread-0 Due to concurrent execution we cannot predict if the runnable will be invoked before or after printing 'done'. The order is non-deterministic, thus making concurrent programming a complex task in larger applications. Threads can be put to sleep for a certain duration. This is quite handy to simulate long running tasks in the subsequent code samples of this article: Runnable runnable = () -> { try { String name = Thread.currentThread().getName(); System.out.println("Foo " + name); TimeUnit.SECONDS.sleep(1); System.out.println("Bar " + name); } catch (InterruptedException e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.start(); When you run the above code you'll notice the one second delay between the first and the second print statement. TimeUnit is a useful enum for working with units of time. Alternatively you can achieve the same by calling Working with the Thread Thread.sleep(1000) . class can be very tedious and error-prone. Due to that reason the Concurrency API has been introduced back in 2004 with the release of Java 5. The API is located in package java.util.concurrent and contains many useful classes for handling concurrent programming. Since that time the Concurrency API has been enhanced with every new Java release and even Java 8 provides new classes and methods for dealing with concurrency. Java 8 Concurrency Tutorial: Threads and Executors 51 Modern Java - A Guide to Java 8 Now let's take a deeper look at one of the most important parts of the Concurrency API - the executor services. Executors The Concurrency API introduces the concept of an ExecutorService as a higher level replacement for working with threads directly. Executors are capable of running asynchronous tasks and typically manage a pool of threads, so we don't have to create new threads manually. All threads of the internal pool will be reused under the hood for revenant tasks, so we can run as many concurrent tasks as we want throughout the life-cycle of our application with a single executor service. This is how the first thread-example looks like using executors: ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(() -> { String threadName = Thread.currentThread().getName(); System.out.println("Hello " + threadName); }); // => Hello pool-1-thread-1 The class Executors provides convenient factory methods for creating different kinds of executor services. In this sample we use an executor with a thread pool of size one. The result looks similar to the above sample but when running the code you'll notice an important difference: the java process never stops! Executors have to be stopped explicitly otherwise they keep listening for new tasks. An ExecutorService provides two methods for that purpose: running tasks to finish while shutdownNow() shutdown() waits for currently interrupts all running tasks and shut the executor down immediately. This is the preferred way how I typically shutdown executors: try { System.out.println("attempt to shutdown executor"); executor.shutdown(); executor.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { System.err.println("tasks interrupted"); } finally { if (!executor.isTerminated()) { System.err.println("cancel non-finished tasks"); } executor.shutdownNow(); System.out.println("shutdown finished"); } Java 8 Concurrency Tutorial: Threads and Executors 52 Modern Java - A Guide to Java 8 The executor shuts down softly by waiting a certain amount of time for termination of currently running tasks. After a maximum of five seconds the executor finally shuts down by interrupting all running tasks. Callables and Futures In addition to Runnable executors support another kind of task named are functional interfaces just like runnables but instead of being void Callable . Callables they return a value. This lambda expression defines a callable returning an integer after sleeping for one second: Callable task = () -> { try { TimeUnit.SECONDS.sleep(1); return 123; } catch (InterruptedException e) { throw new IllegalStateException("task interrupted", e); } }; Callables can be submitted to executor services just like runnables. But what about the callables result? Since doesn't wait until the task completes, the executor service submit() cannot return the result of the callable directly. Instead the executor returns a special result of type Future which can be used to retrieve the actual result at a later point in time. ExecutorService executor = Executors.newFixedThreadPool(1); Future future = executor.submit(task); System.out.println("future done? " + future.isDone()); Integer result = future.get(); System.out.println("future done? " + future.isDone()); System.out.print("result: " + result); After submitting the callable to the executor we first check if the future has already been finished execution via isDone() . I'm pretty sure this isn't the case since the above callable sleeps for one second before returning the integer. Calling the method get() blocks the current thread and waits until the callable completes before returning the actual result 123 . Now the future is finally done and we see the following result on the console: future done? false future done? true result: 123 Futures are tightly coupled to the underlying executor service. Keep in mind that every nonterminated future will throw exceptions if you shutdown the executor: Java 8 Concurrency Tutorial: Threads and Executors 53 Modern Java - A Guide to Java 8 executor.shutdownNow(); future.get(); You might have noticed that the creation of the executor slightly differs from the previous example. We use newFixedThreadPool(1) pool of size one. This is equivalent to to create an executor service backed by a thread- newSingleThreadExecutor() but we could later increase the pool size by simply passing a value larger than one. Timeouts Any call to future.get() will block and wait until the underlying callable has been terminated. In the worst case a callable runs forever - thus making your application unresponsive. You can simply counteract those scenarios by passing a timeout: ExecutorService executor = Executors.newFixedThreadPool(1); Future future = executor.submit(() -> { try { TimeUnit.SECONDS.sleep(2); return 123; } catch (InterruptedException e) { throw new IllegalStateException("task interrupted", e); } }); future.get(1, TimeUnit.SECONDS); Executing the above code results in a TimeoutException : Exception in thread "main" java.util.concurrent.TimeoutException at java.util.concurrent.FutureTask.get(FutureTask.java:205) You might already have guessed why this exception is thrown: We specified a maximum wait time of one second but the callable actually needs two seconds before returning the result. InvokeAll Executors support batch submitting of multiple callables at once via invokeAll() . This method accepts a collection of callables and returns a list of futures. Java 8 Concurrency Tutorial: Threads and Executors 54 Modern Java - A Guide to Java 8 ExecutorService executor = Executors.newWorkStealingPool(); List> callables = Arrays.asList( () -> "task1", () -> "task2", () -> "task3"); executor.invokeAll(callables) .stream() .map(future -> { try { return future.get(); } catch (Exception e) { throw new IllegalStateException(e); } }) .forEach(System.out::println); In this example we utilize Java 8 functional streams in order to process all futures returned by the invocation of invokeAll . We first map each future to its return value and then print each value to the console. If you're not yet familiar with streams read my Java 8 Stream Tutorial. InvokeAny Another way of batch-submitting callables is the method different to invokeAll() invokeAny() which works slightly . Instead of returning future objects this method blocks until the first callable terminates and returns the result of that callable. In order to test this behavior we use this helper method to simulate callables with different durations. The method returns a callable that sleeps for a certain amount of time until returning the given result: Callable callable(String result, long sleepSeconds) { return () -> { TimeUnit.SECONDS.sleep(sleepSeconds); return result; }; } We use this method to create a bunch of callables with different durations from one to three seconds. Submitting those callables to an executor via invokeAny() returns the string result of the fastest callable - in that case task2: Java 8 Concurrency Tutorial: Threads and Executors 55 Modern Java - A Guide to Java 8 ExecutorService executor = Executors.newWorkStealingPool(); List> callables = Arrays.asList( callable("task1", 2), callable("task2", 1), callable("task3", 3)); String result = executor.invokeAny(callables); System.out.println(result); // => task2 The above example uses yet another type of executor created via newWorkStealingPool() This factory method is part of Java 8 and returns an executor of type ForkJoinPool . which works slightly different than normal executors. Instead of using a fixed size thread-pool ForkJoinPools are created for a given parallelism size which per default is the number of available cores of the hosts CPU. ForkJoinPools exist since Java 7 and will be covered in detail in a later tutorial of this series. Let's finish this tutorial by taking a deeper look at scheduled executors. Scheduled Executors We've already learned how to submit and run tasks once on an executor. In order to periodically run common tasks multiple times, we can utilize scheduled thread pools. A ScheduledExecutorService is capable of scheduling tasks to run either periodically or once after a certain amount of time has elapsed. This code sample schedules a task to run after an initial delay of three seconds has passed: ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); Runnable task = () -> System.out.println("Scheduling: " + System.nanoTime()); ScheduledFuture future = executor.schedule(task, 3, TimeUnit.SECONDS); TimeUnit.MILLISECONDS.sleep(1337); long remainingDelay = future.getDelay(TimeUnit.MILLISECONDS); System.out.printf("Remaining Delay: %sms", remainingDelay); Scheduling a task produces a specialized future of type to Future - provides the method getDelay() ScheduledFuture which - in addition to retrieve the remaining delay. After this delay has elapsed the task will be executed concurrently. In order to schedule tasks to be executed periodically, executors provide the two methods scheduleAtFixedRate() and scheduleWithFixedDelay() . The first method is capable of executing tasks with a fixed time rate, e.g. once every second as demonstrated in this example: Java 8 Concurrency Tutorial: Threads and Executors 56 Modern Java - A Guide to Java 8 ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); Runnable task = () -> System.out.println("Scheduling: " + System.nanoTime()); int initialDelay = 0; int period = 1; executor.scheduleAtFixedRate(task, initialDelay, period, TimeUnit.SECONDS); Additionally this method accepts an initial delay which describes the leading wait time before the task will be executed for the first time. Please keep in mind that scheduleAtFixedRate() doesn't take into account the actual duration of the task. So if you specify a period of one second but the task needs 2 seconds to be executed then the thread pool will working to capacity very soon. In that case you should consider using scheduleWithFixedDelay() instead. This method works just like the counterpart described above. The difference is that the wait time period applies between the end of a task and the start of the next task. For example: ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); Runnable task = () -> { try { TimeUnit.SECONDS.sleep(2); System.out.println("Scheduling: " + System.nanoTime()); } catch (InterruptedException e) { System.err.println("task interrupted"); } }; executor.scheduleWithFixedDelay(task, 0, 1, TimeUnit.SECONDS); This example schedules a task with a fixed delay of one second between the end of an execution and the start of the next execution. The initial delay is zero and the tasks duration is two seconds. So we end up with an execution interval of 0s, 3s, 6s, 9s and so on. As you can see scheduleWithFixedDelay() is handy if you cannot predict the duration of the scheduled tasks. This was the first part out of a series of concurrency tutorials. I recommend practicing the shown code samples by your own. You find all code samples from this article on GitHub, so feel free to fork the repo and give me star. I hope you've enjoyed this article. If you have any further questions send me your feedback in the comments below or via Twitter. Part 1: Threads and Executors Part 2: Synchronization and Locks Part 3: Atomic Variables and ConcurrentMap Java 8 Concurrency Tutorial: Threads and Executors 57 Modern Java - A Guide to Java 8 Java 8 Concurrency Tutorial: Synchronization and Locks April 30, 2015 Welcome to the second part of my Java 8 Concurrency Tutorial out of a series of guides teaching multi-threaded programming in Java 8 with easily understood code examples. In the next 15 min you learn how to synchronize access to mutable shared variables via the synchronized keyword, locks and semaphores. Part 1: Threads and Executors Part 2: Synchronization and Locks Part 3: Atomic Variables and ConcurrentMap The majority of concepts shown in this article also work in older versions of Java. However the code samples focus on Java 8 and make heavy use of lambda expressions and new concurrency features. If you're not yet familiar with lambdas I recommend reading my Java 8 Tutorial first. For simplicity the code samples of this tutorial make use of the two helper methods sleep(seconds) and stop(executor) as defined here. Synchronized In the previous tutorial) we've learned how to execute code in parallel via executor services. When writing such multi-threaded code you have to pay particular attention when accessing shared mutable variables concurrently from multiple threads. Let's just say we want to increment an integer which is accessible simultaneously from multiple threads. We define a field count with a method increment() to increase count by one: int count = 0; void increment() { count = count + 1; } When calling this method concurrently from multiple threads we're in serious trouble: Java 8 Concurrency Tutorial: Synchronization and Locks 58 Modern Java - A Guide to Java 8 ExecutorService executor = Executors.newFixedThreadPool(2); IntStream.range(0, 10000) .forEach(i -> executor.submit(this::increment)); stop(executor); System.out.println(count); // 9965 Instead of seeing a constant result count of 10000 the actual result varies with every execution of the above code. The reason is that we share a mutable variable upon different threads without synchronizing the access to this variable which results in a race condition. Three steps have to be performed in order to increment the number: (i) read the current value, (ii) increase this value by one and (iii) write the new value to the variable. If two threads perform these steps in parallel it's possible that both threads perform step 1 simultaneously thus reading the same current value. This results in lost writes so the actual result is lower. In the above sample 35 increments got lost due to concurrent unsynchronized access to count but you may see different results when executing the code by yourself. Luckily Java supports thread-synchronization since the early days via the keyword. We can utilize synchronized synchronized to fix the above race conditions when incrementing the count: synchronized void incrementSync() { count = count + 1; } When using incrementSync() concurrently we get the desired result count of 10000. No race conditions occur any longer and the result is stable with every execution of the code: ExecutorService executor = Executors.newFixedThreadPool(2); IntStream.range(0, 10000) .forEach(i -> executor.submit(this::incrementSync)); stop(executor); System.out.println(count); The synchronized // 10000 keyword is also available as a block statement. void incrementSync() { synchronized (this) { count = count + 1; } } Java 8 Concurrency Tutorial: Synchronization and Locks 59 Modern Java - A Guide to Java 8 Internally Java uses a so called monitor also known as monitor lock or intrinsic lock in order to manage synchronization. This monitor is bound to an object, e.g. when using synchronized methods each method share the same monitor of the corresponding object. All implicit monitors implement the reentrant characteristics. Reentrant means that locks are bound to the current thread. A thread can safely acquire the same lock multiple times without running into deadlocks (e.g. a synchronized method calls another synchronized method on the same object). Locks Instead of using implicit locking via the synchronized supports various explicit locks specified by the Lock keyword the Concurrency API interface. Locks support various methods for finer grained lock control thus are more expressive than implicit monitors. Multiple lock implementations are available in the standard JDK which will be demonstrated in the following sections. ReentrantLock The class ReentrantLock is a mutual exclusion lock with the same basic behavior as the implicit monitors accessed via the synchronized keyword but with extended capabilities. As the name suggests this lock implements reentrant characteristics just as implicit monitors. Let's see how the above sample looks like using ReentrantLock : ReentrantLock lock = new ReentrantLock(); int count = 0; void increment() { lock.lock(); try { count++; } finally { lock.unlock(); } } A lock is acquired via into a try/finally lock() and released via unlock() . It's important to wrap your code block to ensure unlocking in case of exceptions. This method is thread- safe just like the synchronized counterpart. If another thread has already acquired the lock subsequent calls to lock() pause the current thread until the lock has been unlocked. Only one thread can hold the lock at any given time. Locks support various methods for fine grained control as seen in the next sample: Java 8 Concurrency Tutorial: Synchronization and Locks 60 Modern Java - A Guide to Java 8 ExecutorService executor = Executors.newFixedThreadPool(2); ReentrantLock lock = new ReentrantLock(); executor.submit(() -> { lock.lock(); try { sleep(1); } finally { lock.unlock(); } }); executor.submit(() -> { System.out.println("Locked: " + lock.isLocked()); System.out.println("Held by me: " + lock.isHeldByCurrentThread()); boolean locked = lock.tryLock(); System.out.println("Lock acquired: " + locked); }); stop(executor); While the first task holds the lock for one second the second task obtains different information about the current state of the lock: Locked: true Held by me: false Lock acquired: false The method tryLock() as an alternative to lock() tries to acquire the lock without pausing the current thread. The boolean result must be used to check if the lock has actually been acquired before accessing any shared mutable variables. ReadWriteLock The interface ReadWriteLock specifies another type of lock maintaining a pair of locks for read and write access. The idea behind read-write locks is that it's usually safe to read mutable variables concurrently as long as nobody is writing to this variable. So the read-lock can be held simultaneously by multiple threads as long as no threads hold the write-lock. This can improve performance and throughput in case that reads are more frequent than writes. ExecutorService executor = Executors.newFixedThreadPool(2); Map map = new HashMap<>(); ReadWriteLock lock = new ReentrantReadWriteLock(); executor.submit(() -> { lock.writeLock().lock(); try { sleep(1); map.put("foo", "bar"); } finally { lock.writeLock().unlock(); } }); Java 8 Concurrency Tutorial: Synchronization and Locks 61 Modern Java - A Guide to Java 8 The above example first acquires a write-lock in order to put a new value to the map after sleeping for one second. Before this task has finished two other tasks are being submitted trying to read the entry from the map and sleep for one second: Runnable readTask = () -> { lock.readLock().lock(); try { System.out.println(map.get("foo")); sleep(1); } finally { lock.readLock().unlock(); } }; executor.submit(readTask); executor.submit(readTask); stop(executor); When you execute this code sample you'll notice that both read tasks have to wait the whole second until the write task has finished. After the write lock has been released both read tasks are executed in parallel and print the result simultaneously to the console. They don't have to wait for each other to finish because read-locks can safely be acquired concurrently as long as no write-lock is held by another thread. StampedLock Java 8 ships with a new kind of lock called locks just like in the example above. In contrast to StampedLock which also support read and write StampedLock return a stamp represented by a ReadWriteLock long the locking methods of a value. You can use these stamps to either release a lock or to check if the lock is still valid. Additionally stamped locks support another lock mode called optimistic locking. Let's rewrite the last example code to use StampedLock instead of Java 8 Concurrency Tutorial: Synchronization and Locks ReadWriteLock : 62 Modern Java - A Guide to Java 8 ExecutorService executor = Executors.newFixedThreadPool(2); Map map = new HashMap<>(); StampedLock lock = new StampedLock(); executor.submit(() -> { long stamp = lock.writeLock(); try { sleep(1); map.put("foo", "bar"); } finally { lock.unlockWrite(stamp); } }); Runnable readTask = () -> { long stamp = lock.readLock(); try { System.out.println(map.get("foo")); sleep(1); } finally { lock.unlockRead(stamp); } }; executor.submit(readTask); executor.submit(readTask); stop(executor); Obtaining a read or write lock via readLock() or writeLock() returns a stamp which is later used for unlocking within the finally block. Keep in mind that stamped locks don't implement reentrant characteristics. Each call to lock returns a new stamp and blocks if no lock is available even if the same thread already holds a lock. So you have to pay particular attention not to run into deadlocks. Just like in the previous ReadWriteLock example both read tasks have to wait until the write lock has been released. Then both read tasks print to the console simultaneously because multiple reads doesn't block each other as long as no write-lock is held. The next example demonstrates optimistic locking: Java 8 Concurrency Tutorial: Synchronization and Locks 63 Modern Java - A Guide to Java 8 ExecutorService executor = Executors.newFixedThreadPool(2); StampedLock lock = new StampedLock(); executor.submit(() -> { long stamp = lock.tryOptimisticRead(); try { System.out.println("Optimistic Lock Valid: " + lock.validate(stamp)); sleep(1); System.out.println("Optimistic Lock Valid: " + lock.validate(stamp)); sleep(2); System.out.println("Optimistic Lock Valid: " + lock.validate(stamp)); } finally { lock.unlock(stamp); } }); executor.submit(() -> { long stamp = lock.writeLock(); try { System.out.println("Write Lock acquired"); sleep(2); } finally { lock.unlock(stamp); System.out.println("Write done"); } }); stop(executor); An optimistic read lock is acquired by calling tryOptimisticRead() which always returns a stamp without blocking the current thread, no matter if the lock is actually available. If there's already a write lock active the returned stamp equals zero. You can always check if a stamp is valid by calling lock.validate(stamp) . Executing the above code results in the following output: Optimistic Write Lock Optimistic Write done Optimistic Lock Valid: true acquired Lock Valid: false Lock Valid: false The optimistic lock is valid right after acquiring the lock. In contrast to normal read locks an optimistic lock doesn't prevent other threads to obtain a write lock instantaneously. After sending the first thread to sleep for one second the second thread obtains a write lock without waiting for the optimistic read lock to be released. From this point the optimistic read lock is no longer valid. Even when the write lock is released the optimistic read locks stays invalid. So when working with optimistic locks you have to validate the lock every time after accessing any shared mutable variable to make sure the read was still valid. Sometimes it's useful to convert a read lock into a write lock without unlocking and locking again. StampedLock provides the method tryConvertToWriteLock() for that purpose as seen in the next sample: Java 8 Concurrency Tutorial: Synchronization and Locks 64 Modern Java - A Guide to Java 8 ExecutorService executor = Executors.newFixedThreadPool(2); StampedLock lock = new StampedLock(); executor.submit(() -> { long stamp = lock.readLock(); try { if (count == 0) { stamp = lock.tryConvertToWriteLock(stamp); if (stamp == 0L) { System.out.println("Could not convert to write lock"); stamp = lock.writeLock(); } count = 23; } System.out.println(count); } finally { lock.unlock(stamp); } }); stop(executor); The task first obtains a read lock and prints the current value of field But if the current value is zero we want to assign a new value of 23 count to the console. . We first have to convert the read lock into a write lock to not break potential concurrent access by other threads. Calling tryConvertToWriteLock() doesn't block but may return a zero stamp indicating that no write lock is currently available. In that case we call writeLock() to block the current thread until a write lock is available. Semaphores In addition to locks the Concurrency API also supports counting semaphores. Whereas locks usually grant exclusive access to variables or resources, a semaphore is capable of maintaining whole sets of permits. This is useful in different scenarios where you have to limit the amount concurrent access to certain parts of your application. Here's an example how to limit access to a long running task simulated by Java 8 Concurrency Tutorial: Synchronization and Locks sleep(5) : 65 Modern Java - A Guide to Java 8 ExecutorService executor = Executors.newFixedThreadPool(10); Semaphore semaphore = new Semaphore(5); Runnable longRunningTask = () -> { boolean permit = false; try { permit = semaphore.tryAcquire(1, TimeUnit.SECONDS); if (permit) { System.out.println("Semaphore acquired"); sleep(5); } else { System.out.println("Could not acquire semaphore"); } } catch (InterruptedException e) { throw new IllegalStateException(e); } finally { if (permit) { semaphore.release(); } } } IntStream.range(0, 10) .forEach(i -> executor.submit(longRunningTask)); stop(executor); The executor can potentially run 10 tasks concurrently but we use a semaphore of size 5, thus limiting concurrent access to 5. It's important to use a try/finally block to properly release the semaphore even in case of exceptions. Executing the above code results in the following output: Semaphore Semaphore Semaphore Semaphore Semaphore Could not Could not Could not Could not Could not acquired acquired acquired acquired acquired acquire semaphore acquire semaphore acquire semaphore acquire semaphore acquire semaphore The semaphores permits access to the actual long running operation simulated by sleep(5) up to a maximum of 5. Every subsequent call to tryAcquire() elapses the maximum wait timeout of one second, resulting in the appropriate console output that no semaphore could be acquired. This was the second part out of a series of concurrency tutorials. More parts will be released in the near future, so stay tuned. As usual you find all code samples from this article on GitHub, so feel free to fork the repo and try it by your own. I hope you've enjoyed this article. If you have any further questions send me your feedback in the comments below. You should also follow me on Twitter for more dev-related stuff! Java 8 Concurrency Tutorial: Synchronization and Locks 66 Modern Java - A Guide to Java 8 Part 1: Threads and Executors Part 2: Synchronization and Locks Part 3: Atomic Variables and ConcurrentMap Java 8 Concurrency Tutorial: Synchronization and Locks 67 Modern Java - A Guide to Java 8 Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap May 22, 2015 Welcome to the third part of my tutorial series about multi-threaded programming in Java 8. This tutorial covers two important parts of the Concurrency API: Atomic Variables and Concurrent Maps. Both have been greatly improved with the introduction of lambda expressions and functional programming in the latest Java 8 release. All those new features are described with a bunch of easily understood code samples. Enjoy! Part 1: Threads and Executors Part 2: Synchronization and Locks Part 3: Atomic Variables and ConcurrentMap For simplicity the code samples of this tutorial make use of the two helper methods sleep(seconds) and stop(executor) as defined here. AtomicInteger The package java.concurrent.atomic contains many useful classes to perform atomic operations. An operation is atomic when you can safely perform the operation in parallel on multiple threads without using the synchronized keyword or locks as shown in my previous tutorial. Internally, the atomic classes make heavy use of compare-and-swap (CAS), an atomic instruction directly supported by most modern CPUs. Those instructions usually are much faster than synchronizing via locks. So my advice is to prefer atomic classes over locks in case you just have to change a single mutable variable concurrently. Now let's pick one of the atomic classes for a few examples: AtomicInteger AtomicInteger atomicInt = new AtomicInteger(0); ExecutorService executor = Executors.newFixedThreadPool(2); IntStream.range(0, 1000) .forEach(i -> executor.submit(atomicInt::incrementAndGet)); stop(executor); System.out.println(atomicInt.get()); // => 1000 Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap 68 Modern Java - A Guide to Java 8 By using AtomicInteger as a replacement for we're able to increment the number Integer concurrently in a thread-safe manor without synchronizing the access to the variable. The method incrementAndGet() is an atomic operation so we can safely call this method from multiple threads. AtomicInteger supports various kinds of atomic operations. The method updateAndGet() accepts a lambda expression in order to perform arbitrary arithmetic operations upon the integer: AtomicInteger atomicInt = new AtomicInteger(0); ExecutorService executor = Executors.newFixedThreadPool(2); IntStream.range(0, 1000) .forEach(i -> { Runnable task = () -> atomicInt.updateAndGet(n -> n + 2); executor.submit(task); }); stop(executor); System.out.println(atomicInt.get()); The method accumulateAndGet() IntBinaryOperator // => 2000 accepts another kind of lambda expression of type . We use this method to sum up all values from 0 to 1000 concurrently in the next sample: AtomicInteger atomicInt = new AtomicInteger(0); ExecutorService executor = Executors.newFixedThreadPool(2); IntStream.range(0, 1000) .forEach(i -> { Runnable task = () -> atomicInt.accumulateAndGet(i, (n, m) -> n + m); executor.submit(task); }); stop(executor); System.out.println(atomicInt.get()); // => 499500 Other useful atomic classes are AtomicBoolean, AtomicLong and AtomicReference. LongAdder The class LongAdder as an alternative to AtomicLong can be used to consecutively add values to a number. Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap 69 Modern Java - A Guide to Java 8 ExecutorService executor = Executors.newFixedThreadPool(2); IntStream.range(0, 1000) .forEach(i -> executor.submit(adder::increment)); stop(executor); System.out.println(adder.sumThenReset()); LongAdder provides methods and add() // => 1000 increment() just like the atomic number classes and is also thread-safe. But instead of summing up a single result this class maintains a set of variables internally to reduce contention over threads. The actual result can be retrieved by calling sum() or sumThenReset() . This class is usually preferable over atomic numbers when updates from multiple threads are more common than reads. This is often the case when capturing statistical data, e.g. you want to count the number of requests served on a web server. The drawback of LongAdder is higher memory consumption because a set of variables is held in-memory. LongAccumulator LongAccumulator is a more generalized version of LongAdder. Instead of performing simple add operations the class LongBinaryOperator LongAccumulator builds around a lambda expression of type as demonstrated in this code sample: LongBinaryOperator op = (x, y) -> 2 * x + y; LongAccumulator accumulator = new LongAccumulator(op, 1L); ExecutorService executor = Executors.newFixedThreadPool(2); IntStream.range(0, 10) .forEach(i -> executor.submit(() -> accumulator.accumulate(i))); stop(executor); System.out.println(accumulator.getThenReset()); We create a LongAccumulator with the function every call to accumulate(i) // => 2539 2 * x + y and an initial value of one. With both the current result and the value i are passed as parameters to the lambda expression. A LongAccumulator just like LongAdder maintains a set of variables internally to reduce contention over threads. ConcurrentMap Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap 70 Modern Java - A Guide to Java 8 The interface ConcurrentMap extends the map interface and defines one of the most useful concurrent collection types. Java 8 introduces functional programming by adding new methods to this interface. In the next code snippets we use the following sample map to demonstrates those new methods: ConcurrentMap map = new ConcurrentHashMap<>(); map.put("foo", "bar"); map.put("han", "solo"); map.put("r2", "d2"); map.put("c3", "p0"); The method forEach() accepts a lambda expression of type BiConsumer with both the key and value of the map passed as parameters. It can be used as a replacement to for-each loops to iterate over the entries of the concurrent map. The iteration is performed sequentially on the current thread. map.forEach((key, value) -> System.out.printf("%s = %s\n", key, value)); The method given key. At least for the just like puts a new value into the map only if no value exists for the putIfAbsent() put() ConcurrentHashMap implementation of this method is thread-safe so you don't have to synchronize when accessing the map concurrently from different threads: String value = map.putIfAbsent("c3", "p1"); System.out.println(value); // p0 The method getOrDefault() returns the value for the given key. In case no entry exists for this key the passed default value is returned: String value = map.getOrDefault("hi", "there"); System.out.println(value); // there The method replaceAll() accepts a lambda expression of type BiFunction . BiFunctions take two parameters and return a single value. In this case the function is called with the key and the value of each map entry and returns a new value to be assigned for the current key: map.replaceAll((key, value) -> "r2".equals(key) ? "d3" : value); System.out.println(map.get("r2")); // d3 Instead of replacing all values of the map compute() let's us transform a single entry. The method accepts both the key to be computed and a bi-function to specify the transformation of the value. Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap 71 Modern Java - A Guide to Java 8 map.compute("foo", (key, value) -> value + value); System.out.println(map.get("foo")); // barbar In addition to compute() two variants exist: computeIfAbsent() and computeIfPresent() . The functional parameters of these methods only get called if the key is absent or present respectively. Finally, the method merge() can be utilized to unify a new value with an existing value in the map. Merge accepts a key, the new value to be merged into the existing entry and a bifunction to specify the merging behavior of both values: map.merge("foo", "boo", (oldVal, newVal) -> newVal + " was " + oldVal); System.out.println(map.get("foo")); // boo was foo ConcurrentHashMap All those methods above are part of the ConcurrentMap interface, thereby available to all implementations of that interface. In addition the most important implementation ConcurrentHashMap has been further enhanced with a couple of new methods to perform parallel operations upon the map. Just like parallel streams those methods use a special ForkJoinPool.commonPool() ForkJoinPool available via in Java 8. This pool uses a preset parallelism which depends on the number of available cores. Four CPU cores are available on my machine which results in a parallelism of three: System.out.println(ForkJoinPool.getCommonPoolParallelism()); // 3 This value can be decreased or increased by setting the following JVM parameter: -Djava.util.concurrent.ForkJoinPool.common.parallelism=5 We use the same example map for demonstrating purposes but this time we work upon the concrete implementation ConcurrentHashMap instead of the interface ConcurrentMap , so we can access all public methods from this class: ConcurrentHashMap map = new ConcurrentHashMap<>(); map.put("foo", "bar"); map.put("han", "solo"); map.put("r2", "d2"); map.put("c3", "p0"); Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap 72 Modern Java - A Guide to Java 8 Java 8 introduces three kinds of parallel operations: forEach , search and reduce . Each of those operations are available in four forms accepting functions with keys, values, entries and key-value pair arguments. All of those methods use a common first argument called parallelismThreshold . This threshold indicates the minimum collection size when the operation should be executed in parallel. E.g. if you pass a threshold of 500 and the actual size of the map is 499 the operation will be performed sequentially on a single thread. In the next examples we use a threshold of one to always force parallel execution for demonstrating purposes. ForEach The method is capable of iterating over the key-value pairs of the map in forEach() parallel. The lambda expression of type BiConsumer is called with the key and value of the current iteration step. In order to visualize parallel execution we print the current threads name to the console. Keep in mind that in my case the underlying ForkJoinPool uses up to a maximum of three threads. map.forEach(1, (key, value) -> System.out.printf("key: %s; value: %s; thread: %s\n", key, value, Thread.currentThread().getName())); // // // // key: key: key: key: r2; value: d2; thread: main foo; value: bar; thread: ForkJoinPool.commonPool-worker-1 han; value: solo; thread: ForkJoinPool.commonPool-worker-2 c3; value: p0; thread: main Search The method search() accepts a current key-value pair or null BiFunction returning a non-null search result for the if the current iteration doesn't match the desired search criteria. As soon as a non-null result is returned further processing is suppressed. Keep in mind that ConcurrentHashMap is unordered. The search function should not depend on the actual processing order of the map. If multiple entries of the map match the given search function the result may be non-deterministic. String result = map.search(1, (key, value) -> { System.out.println(Thread.currentThread().getName()); if ("foo".equals(key)) { return value; } return null; }); System.out.println("Result: " + result); // // // // ForkJoinPool.commonPool-worker-2 main ForkJoinPool.commonPool-worker-3 Result: bar Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap 73 Modern Java - A Guide to Java 8 Here's another example searching solely on the values of the map: String result = map.searchValues(1, value -> { System.out.println(Thread.currentThread().getName()); if (value.length() > 3) { return value; } return null; }); System.out.println("Result: " + result); // // // // // ForkJoinPool.commonPool-worker-2 main main ForkJoinPool.commonPool-worker-1 Result: solo Reduce The method reduce() expressions of type already known from Java 8 Streams accepts two lambda BiFunction . The first function transforms each key-value pair into a single value of any type. The second function combines all those transformed values into a single result, ignoring any possible null values. String result = map.reduce(1, (key, value) -> { System.out.println("Transform: " + Thread.currentThread().getName()); return key + "=" + value; }, (s1, s2) -> { System.out.println("Reduce: " + Thread.currentThread().getName()); return s1 + ", " + s2; }); System.out.println("Result: " + result); // // // // // // // // Transform: ForkJoinPool.commonPool-worker-2 Transform: main Transform: ForkJoinPool.commonPool-worker-3 Reduce: ForkJoinPool.commonPool-worker-3 Transform: main Reduce: main Reduce: main Result: r2=d2, c3=p0, han=solo, foo=bar I hope you've enjoyed reading the third part of my tutorial series about Java 8 Concurrency. The code samples from this tutorial are hosted on GitHub along with many other Java 8 code snippets. You're welcome to fork the repo and try it by your own. If you want to support my work, please share this tutorial with your friends. You should also follow me on Twitter as I constantly tweet about Java and programming related stuff. Part 1: Threads and Executors Part 2: Synchronization and Locks Part 3: Atomic Variables and ConcurrentMap Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap 74 Modern Java - A Guide to Java 8 Java 8 API by Example: Strings, Numbers, Math and Files March 25, 2015 Plenty of tutorials and articles cover the most important changes in Java 8 like lambda expressions and functional streams. But furthermore many existing classes have been enhanced in the JDK 8 API with useful features and methods. This article covers some of those smaller changes in the Java 8 API - each described with easily understood code samples. Let's take a deeper look into Strings, Numbers, Math and Files. Slicing Strings Two new methods are available on the String class: join and chars . The first method joins any number of strings into a single string with the given delimiter: String.join(":", "foobar", "foo", "bar"); // => foobar:foo:bar The second method chars creates a stream for all characters of the string, so you can use stream operations upon those characters: "foobar:foo:bar" .chars() .distinct() .mapToObj(c -> String.valueOf((char)c)) .sorted() .collect(Collectors.joining()); // => :abfor Not only strings but also regex patterns now benefit from streams. Instead of splitting strings into streams for each character we can split strings for any pattern and create a stream to work upon as shown in this example: Pattern.compile(":") .splitAsStream("foobar:foo:bar") .filter(s -> s.contains("bar")) .sorted() .collect(Collectors.joining(":")); // => bar:foobar Additionally regex patterns can be converted into predicates. Those predicates can for example be used to filter a stream of strings: Java 8 API by Example: Strings, Numbers, Math and Files 75 Modern Java - A Guide to Java 8 Pattern pattern = Pattern.compile(".*@gmail\\.com"); Stream.of("bob@gmail.com", "alice@hotmail.com") .filter(pattern.asPredicate()) .count(); // => 1 The above pattern accepts any string which ends with Java 8 Predicate @gmail.com and is then used as a to filter a stream of email addresses. Crunching Numbers Java 8 adds additional support for working with unsigned numbers. Numbers in Java had always been signed. Let's look at An int Integer for example: represents a maximum of 2³² binary digits. Numbers in Java are per default signed, so the last binary digit represents the sign (0 = positive, 1 = negative). Thus the maximum positive signed int is 2³¹ - 1 starting with the decimal zero. You can access this value via Integer.MAX_VALUE System.out.println(Integer.MAX_VALUE); System.out.println(Integer.MAX_VALUE + 1); : // 2147483647 // -2147483648 Java 8 adds support for parsing unsigned ints. Let's see how this works: long maxUnsignedInt = (1l << 32) - 1; String string = String.valueOf(maxUnsignedInt); int unsignedInt = Integer.parseUnsignedInt(string, 10); String string2 = Integer.toUnsignedString(unsignedInt, 10); As you can see it's now possible to parse the maximum possible unsigned number 2³² - 1 into an integer. And you can also convert this number back into a string representing the unsigned number. This wasn't possible before with parseInt as this example demonstrates: try { Integer.parseInt(string, 10); } catch (NumberFormatException e) { System.err.println("could not parse signed int of " + maxUnsignedInt); } The number is not parseable as a signed int because it exceeds the maximum of 2³¹ - 1. Do the Math Java 8 API by Example: Strings, Numbers, Math and Files 76 Modern Java - A Guide to Java 8 The utility class Math has been enhanced by a couple of new methods for handling number overflows. What does that mean? We've already seen that all number types have a maximum value. So what happens when the result of an arithmetic operation doesn't fit into its size? System.out.println(Integer.MAX_VALUE); System.out.println(Integer.MAX_VALUE + 1); // 2147483647 // -2147483648 As you can see a so called integer overflow happens which is normally not the desired behavior. Java 8 adds support for strict math to handle this problem. Math couple of methods who all ends with . Those methods handle overflows properly by throwing an exact , e.g. addExact ArithmeticException has been extended by a when the result of the operation doesn't fit into the number type: try { Math.addExact(Integer.MAX_VALUE, 1); } catch (ArithmeticException e) { System.err.println(e.getMessage()); // => integer overflow } The same exception might be thrown when trying to convert longs to int via toIntExact : try { Math.toIntExact(Long.MAX_VALUE); } catch (ArithmeticException e) { System.err.println(e.getMessage()); // => integer overflow } Working with Files The utility class Files was first introduced in Java 7 as part of Java NIO. The JDK 8 API adds a couple of additional methods which enables us to use functional streams with files. Let's deep-dive into a couple of code samples. Listing files The method Files.list operations like filter streams all paths for a given directory, so we can use stream and sorted upon the contents of the file system. Java 8 API by Example: Strings, Numbers, Math and Files 77 Modern Java - A Guide to Java 8 try (Stream stream = Files.list(Paths.get(""))) { String joined = stream .map(String::valueOf) .filter(path -> !path.startsWith(".")) .sorted() .collect(Collectors.joining("; ")); System.out.println("List: " + joined); } The above example lists all files for the current working directory, then maps each path to it's string representation. The result is then filtered, sorted and finally joined into a string. If you're not yet familiar with functional streams you should read my Java 8 Stream Tutorial. You might have noticed that the creation of the stream is wrapped into a try/with statement. Streams implement AutoCloseable and in this case we really have to close the stream explicitly since it's backed by IO operations. > The returned stream encapsulates a DirectoryStream. If timely disposal of file system resources is required, the try-with-resources construct should be used to ensure that the stream's close method is invoked after the stream operations are completed. Finding files The next example demonstrates how to find files in a directory or it's sub-directories. Path start = Paths.get(""); int maxDepth = 5; try (Stream stream = Files.find(start, maxDepth, (path, attr) -> String.valueOf(path).endsWith(".js"))) { String joined = stream .sorted() .map(String::valueOf) .collect(Collectors.joining("; ")); System.out.println("Found: " + joined); } The method point and find maxDepth accepts three arguments: The directory path start is the initial starting defines the maximum folder depth to be searched. The third argument is a matching predicate and defines the search logic. In the above example we search for all JavaScript files (filename ends with .js). We can achieve the same behavior by utilizing the method Files.walk . Instead of passing a search predicate this method just walks over any file. Java 8 API by Example: Strings, Numbers, Math and Files 78 Modern Java - A Guide to Java 8 Path start = Paths.get(""); int maxDepth = 5; try (Stream stream = Files.walk(start, maxDepth)) { String joined = stream .map(String::valueOf) .filter(path -> path.endsWith(".js")) .sorted() .collect(Collectors.joining("; ")); System.out.println("walk(): " + joined); } In this example we use the stream operation filter to achieve the same behavior as in the previous example. Reading and writing files Reading text files into memory and writing strings into a text file in Java 8 is finally a simple task. No messing around with readers and writers. The method Files.readAllLines reads all lines of a given file into a list of strings. You can simply modify this list and write the lines into another file via Files.write : List lines = Files.readAllLines(Paths.get("res/nashorn1.js")); lines.add("print('foobar');"); Files.write(Paths.get("res/nashorn1-modified.js"), lines); Please keep in mind that those methods are not very memory-efficient because the whole file will be read into memory. The larger the file the more heap-size will be used. As an memory-efficient alternative you could use the method Files.lines . Instead of reading all lines into memory at once, this method reads and streams each line one by one via functional streams. try (Stream stream = Files.lines(Paths.get("res/nashorn1.js"))) { stream .filter(line -> line.contains("print")) .map(String::trim) .forEach(System.out::println); } If you need more fine-grained control you can instead construct a new buffered reader: Path path = Paths.get("res/nashorn1.js"); try (BufferedReader reader = Files.newBufferedReader(path)) { System.out.println(reader.readLine()); } Or in case you want to write to a file simply construct a buffered writer instead: Java 8 API by Example: Strings, Numbers, Math and Files 79 Modern Java - A Guide to Java 8 Path path = Paths.get("res/output.js"); try (BufferedWriter writer = Files.newBufferedWriter(path)) { writer.write("print('Hello World');"); } Buffered readers also have access to functional streams. The method lines construct a functional stream upon all lines denoted by the buffered reader: Path path = Paths.get("res/nashorn1.js"); try (BufferedReader reader = Files.newBufferedReader(path)) { long countPrints = reader .lines() .filter(line -> line.contains("print")) .count(); System.out.println(countPrints); } So as you can see Java 8 provides three simple ways to read the lines of a text file, making text file handling quite convenient. Unfortunately you have to close functional file streams explicitly with try/with statements which makes the code samples still kinda cluttered. I would have expected that functional streams auto-close when calling a terminal operation like count or collect since you cannot call terminal operations twice on the same stream anyway. I hope you've enjoyed this article. All code samples are hosted on GitHub along with plenty of other code snippets from all the Java 8 articles of my blog. If this post was kinda useful to you feel free to star the repo and follow me on Twitter. Keep on coding! Java 8 API by Example: Strings, Numbers, Math and Files 80 Modern Java - A Guide to Java 8 Avoiding Null Checks in Java 8 March 15, 2015 How to prevent the famous NullPointerException in Java? This is one of the key questions every Java beginner will ask sooner or later. But also intermediate and expert programmers get around this error every now and then. It's by far the most prevalent kind of error in Java and many other programming languages as well. Tony Hoare, the inventor of the null reference apologized in 2009 and denotes this kind of errors as his billion-dollar mistake. > I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. Anyways, we have to deal with it. So what can we do to prevent NullPointerExceptions at all? Well, the obvious answer is to add null checks all around the place. Since null checks are kinda cumbersome and painful many languages add special syntax for handling null checks via null coalescing operators - also known as elvis operator in languages like Groovy or Kotlin. Unfortunately Java doesn't provide such a syntactic sugar. But luckily things get better in Java Version 8. This post describes a couple of techniques how to prevent writing needless null checks by utilizing new features of Java 8 like lambda expressions. Improving Null Safety in Java 8 I've already shown in another post how we can utilize the Optional type of Java 8 to prevent null checks. Here's the example code from the original post. Assuming we have a hierarchical class structure like this: Avoiding Null Checks in Java 8 81 Modern Java - A Guide to Java 8 class Outer { Nested nested; Nested getNested() { return nested; } } class Nested { Inner inner; Inner getInner() { return inner; } } class Inner { String foo; String getFoo() { return foo; } } Resolving a deep nested path in this structure can be kinda awkward. We have to write a bunch of null checks to make sure not to raise a NullPointerException : Outer outer = new Outer(); if (outer != null && outer.nested != null && outer.nested.inner != null) { System.out.println(outer.nested.inner.foo); } We can get rid of all those null checks by utilizing the Java 8 map accepts a lambda expression of type result into an Optional Function Optional type. The method and automatically wraps each function . That enables us to pipe multiple map operations in a row. Null checks are automatically handled under the hood. Optional.of(new Outer()) .map(Outer::getNested) .map(Nested::getInner) .map(Inner::getFoo) .ifPresent(System.out::println); An alternative way to achieve the same behavior is by utilizing a supplier function to resolve the nested path: Outer obj = new Outer(); resolve(() -> obj.getNested().getInner().getFoo()); .ifPresent(System.out::println); Calling obj.getNested().getInner().getFoo()) might throw a case the exception will be caught and the method returns Avoiding Null Checks in Java 8 NullPointerException Optional.empty() . In this . 82 Modern Java - A Guide to Java 8 public static Optional resolve(Supplier resolver) { try { T result = resolver.get(); return Optional.ofNullable(result); } catch (NullPointerException e) { return Optional.empty(); } } Please keep in mind that both solutions are probably not as performant as traditional null checks. In most cases that shouldn't be much of an issue. As usual the above code samples are hosted on GitHub. Happy coding! > UPDATE: I've updated the code samples thanks to a hint from Zukhramm on Reddit. Avoiding Null Checks in Java 8 83 Modern Java - A Guide to Java 8 Fixing Java 8 Stream Gotchas with IntelliJ IDEA March 05, 2015 Java 8 has been released almost one year ago in March 2014. At Pondus we've managed to update all of our production servers to this new version back in May 2014. Since then we've migrated major parts of our code base to lambda expressions, streams and the new Date API. We also use Nashorn to dynamically script parts of our application which may change during runtime. The most used feature besides lambdas is the new Stream API. Collection operations are all around the place in almost any codebase I've ever seen. And Streams are a great way to improve code readability of all those collection crunching. But one thing about streams really bothers me: Streams only provide a few terminal operations like and findFirst directly while others are only accessible via . There's a utility class Collectors, providing a bunch of convenient collectors like collect toList reduce , toSet , joining and groupingBy . For example this code filters over a collection of strings and creates a new list: stringCollection .stream() .filter(e -> e.startsWith("a")) .collect(Collectors.toList()); After migrating a project with 300k lines of code to streams I can say that and groupingBy toList , toSet are by far the most used terminal operations in our project. So I really cannot understand the design decision not to integrate those methods directly into the Stream interface so you could just write: stringCollection .stream() .filter(e -> e.startsWith("a")) .toList(); This might look like a minor imperfection at first but it gets really annoying if you have to use this kind of stuff over and over again. There's a method toArray() but no toList() . So I really hope some of the more convenient collectors will make it's way into the Fixing Java 8 Stream Gotchas with IntelliJ IDEA Stream interface in Java 9. Brian? _ 84 Modern Java - A Guide to Java 8 > As a side note: Stream.js is a JavaScript port of the Java 8 Streams API for the browser and addresses the described issue nicely. All important terminal operations are directly accessible on the stream itself for convenience. See the API doc for details. Anyways. IntelliJ IDEA claims to be the most intelligent Java IDE. So let's see how we can utilize IDEA to solve this problem for us. IntelliJ IDEA to the rescue IntelliJ IDEA comes with a handy feature called Live Templates. If you don't already know what it is: Live Templates are shortcuts for commonly used code snippets. E.g. you type sout + tabulator and IDEA inserts the code snippet System.out.println() . Read here to learn more about it. How does Live Templates help with the problem described above? Actually we can simply create our own Live Templates for all the commonly used default Stream collectors. E.g. we can create a Live Template with the abbreviation .collect(Collectors.toList()) .toList to insert the appropriate collector automatically. This is how it looks like in action: Set up your own Live Templates Let's see how we can set this up. First go to Settings and choose Live Templates in the menu to the left. You can also use the handy filter input at the top left of the dialog. Fixing Java 8 Stream Gotchas with IntelliJ IDEA 85 Modern Java - A Guide to Java 8 Next we can create a new group called Stream via the + icon on the right. Next we add all of our stream-related Live Templates to this group. I'm using the default collectors toSet , groupingBy and join toList , quite commonly, so I create a new Live Template for each of those methods. This part is important: After adding a new Live Template you have to specify the applicable context at the bottom of the dialog. You have to choose Java → Other. Afterwards you define the abbreviation, a description and the actual template code. // Abbreviation: .toList .collect(Collectors.toList()) // Abbreviation: .toSet .collect(Collectors.toSet()) // Abbreviation: .join .collect(Collectors.joining("$END$")) // Abbreviation: .groupBy .collect(Collectors.groupingBy(e -> $END$)) The special variable $END$ determines the cursors position after using the template, so you can directly start typing at this position, e.g. to define the joining delimiter. Fixing Java 8 Stream Gotchas with IntelliJ IDEA 86 Modern Java - A Guide to Java 8 > Hint: You should enable the option "Add unambiguous imports on the fly" so IDEA automatically adds an import statement to java.util.stream.Collectors . The option is located in: Editor → General → Auto Import Let's see those two templates in action: Join GroupBy Live Templates in IntelliJ IDEA are an extremely versatile and powerful tool. You can greatly increase your coding productivity with it. Do you know other examples where Live Templates can save your live? Let me know! Still not satisfied? Learn everything you ever wanted to know about Java 8 Streams in my Streams Tutorial. Happy coding. Fixing Java 8 Stream Gotchas with IntelliJ IDEA 87 Modern Java - A Guide to Java 8 Using Backbone.js with Nashorn April 07, 2014 This example demonstrates how to use Backbone.js models with the Java 8 Nashorn Javascript Engine. First released in March 2014 as part of Java SE 8, Nashorn extends Javas capabilities by running javascript code natively on the JVM. For java web developers Nashorn might be especially useful for re-using existing client-side code on the java server. Traditionally Node.js was in a clear advantage, but Nashorns possibilities might close the gap to the JVM. When working with modern javascript MVC frameworks like Backbone.js for HTML5 frontends, more and more code moves from the server back-end to the web front-end. This approach can greatly increase the user experience because you safe a lot of serverroundtrips when using business logic from your views. Backbone enables you to define model classes which can be bound to views (e.g. HTML forms). Backbone keeps track of updating the model when the user interacts with the UI and vice versa. It also aids you by synchronizing your model with the server, e.g. by calling the appropriate method of your REST handler on the server side. So you end up implementing business logic in your front-end code, leaving your server model responsible for persisting data. Reusing backbone models on the server side is quite easy with Nashorn, as the following example will demonstrate. Before we start make sure you're familiar with writing javascript for the Nashorn Engine by reading my Nashorn Tutorial. The Java Model First, we define a domain class Product in java code. This class might be used for CRUD database operations (saving to and loading from a datasource). Keep in mind that this class is a dumb Java Bean without any business logic applied, because we want our front-end to be capable of executing the business logic right from the UI. class Product { String name; double price; int stock; double valueOfGoods; } The Backbone Model Using Backbone.js with Nashorn 88 Modern Java - A Guide to Java 8 Now we define the backbone model as the counter-part of our java bean. The backbone model Product uses the same data-structure as the java bean, since this is the data we might want to persist on the java server. The backbone model also implements the business logic: The method calculates the value of all products by multiplying price changes the property valueOfGoods stock with price getValueOfGoods . Each time stock or must be re-calculated. var Product = Backbone.Model.extend({ defaults: { name: '', stock: 0, price: 0.0, valueOfGoods: 0.0 }, initialize: function() { this.on('change:stock change:price', function() { var stock = this.get('stock'); var price = this.get('price'); var valueOfGoods = this.getValueOfGoods(stock, price); this.set('valueOfGoods', valueOfGoods); }); }, getValueOfGoods: function(stock, price) { return stock * price; } }); Since the backbone model doesn't use any Nashorn language extensions, we can safely use the same code both on the client (Browser) and on the server (Java) side. Keep in mind that I deliberately chose a really simple function for demonstrating purposes only. Real business logic should be more complex. Putting both together The next goal is to re-use the backbone model from Nashorn, e.g. on the java server. We want to achieve the following behavior: bind all properties from the java bean to the backbone model; calculate the property valueOfGoods ; pass the result back to java. First, we create a new script to be evaluated solely by Nashorn, so we can safely use Nashorn extensions here: Using Backbone.js with Nashorn 89 Modern Java - A Guide to Java 8 load('http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js'); load('http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min.js'); load('product-backbone-model.js'); var calculate = function(javaProduct) { var model = new Product(); model.set('name', javaProduct.name); model.set('price', javaProduct.price); model.set('stock', javaProduct.stock); return model.attributes; }; The script first loads the relevant external scripts Underscore and Backbone (Underscore is a pre-requirement for Backbone) and our The function calculate created backbone accepts a Product backbone model as defined above. java bean, binds all properties to a newly and returns all attributes of the model back to the caller. By Product setting the properties Product stock and price on the backbone model, property valueOfGoods will automatically be calculated due to the event handler registered in the models initialize constructor function. Finally, we call the function calculate from java: Product product = new Product(); product.setName("Rubber"); product.setPrice(1.99); product.setStock(1337); ScriptObjectMirror result = (ScriptObjectMirror) invocable.invokeFunction("calculate", product); System.out.println(result.get("name") + ": " + result.get("valueOfGoods")); // Rubber: 2660.63 We create a new method Product getValueOfGoods java bean and pass it to the javascript function. As a result the will be triggered, so we can read the property valueOfGoods from the returning object. Conclusion Reusing existing javascript libraries on the Nashorn Engine is quite easy. Backbone is great for building complex HTML5 front-ends. In my opinion Nashorn and the JVM now is a great alternative to Node.js, since you can make use of the whole Java eco-system in your Nashorn codebase, such as the whole JDK API and all available libraries and tools. Keep in mind that you're not tight to the Java Language when working with Nashorn - think Scala, Groovy, Clojure or even pure Javascript via jjs . The runnable source code from this article is hosted on GitHub (see this file). Feel free to fork the repository or send me your feedback via Twitter. Using Backbone.js with Nashorn 90


Source Exif Data:
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.4
Linearized                      : No
Author                          : wizardforcel
Create Date                     : 2016:04:16 02:33:35+00:00
Producer                        : calibre 2.41.0 [http://calibre-ebook.com]
Description                     : Author: winterbe
Title                           : Modern Java - A Guide to Java 8
Creator                         : wizardforcel
Publisher                       : GitBook
Subject                         : 
Language                        : en
Metadata Date                   : 2016:04:16 02:33:35.519649+00:00
Timestamp                       : 2016:04:16 02:33:31.995160+00:00
Page Count                      : 90
EXIF Metadata provided by EXIF.tools

Navigation menu