Java 8 Streams - java.ociweb.comjava.ociweb.com/javasig/knowledgebase/2015-01/Java8Streams.pdf ·...

Preview:

Citation preview

Java 8 StreamsSaint Louis Java Users Group

8 January, 2015

Charles A Sharp csharp@ociweb.com

While adding lambda expressions to the language is a huge step forward, developers get their work done every day by using the core libraries, so the language evolution effort was paired with a library evolution effort so that users could start using the new features on day one. The centerpiece of the new library features is the Stream abstraction, which provides powerful facilities for aggregate operations on data sets, and has been deeply integrated with the existing collection classes as well as other JDK classes.

— State of the Lambda: Library Edition

Summary

grep 'Error' /var/log/err_log | \

sed 's/^.*Error//' | \

sort | \

uniq -c > /tmp/error_count

— Jennifer Egan, A Visit from the Goon Squad

“There are so many ways to go wrong," Lulu said. "All we've got are metaphors, and

they're never exactly right. You can't ever just Say. The. Thing.”

In computer science, a stream is a sequence of data elements made available over time.

https://en.wikipedia.org/wiki/Stream_(computing)

In object-oriented programming, input streams are generally implemented as iterators.

.

.

.

Iterators? Say, what? Well, no. I mean, yes.

In the Scheme language and some others, a stream is a lazily evaluated or delayed sequence of data elements. A stream can be used similarly to a list, but later elements are only calculated when needed. Streams can therefore represent infinite sequences and series.

https://en.wikipedia.org/wiki/Stream_(computing)

Srsly. What is a Java 8 Stream?

Source

[IntermediateOperations.]*

TerminalOperation

Streams vs. Collections• No Storage — carry values from a source.

• Functional in nature — do not modify the source.

• Laziness-seeking — efficient short-circuits.

• Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.

• Possibly unbounded — A stream need not be finite, unlike a collection.

• Consumable — one time only.

Streams-relevant Java 8 Review

Functional Interface…• Functional interfaces provide target types for lambda

expressions and method references. Each functional interface has a single abstract method, called the functional method for that functional interface, to which the lambda expression's parameter and return types are matched or adapted.

• The type of the functional interface is inferred from the context and is known as the target type.

… Functional Interface• @FunctionalInterface

An annotation that indicates an interface type declaration is intended to be a functional interface. (… However, the compiler will treat any interface meeting the definition of a functional interface as a functional interface regardless of whether or not a FunctionalInterface annotation is present on the interface declaration.)

• Functional Interfaces in Java 8 are listed in the package description of java.util.function.

Function Shapes• Function<T, R> (unary function from T to R)

• Consumer<T> (unary function from T to void)

• Predicate<T> (unary function from T to boolean)

• Supplier<R> (nilary function providing R)

• UnaryOperator<T> (a function from T to T)

• BinaryOperator<T, T> (a function from (T,T) to T)

Method ReferenceEasy-to-read Lambda expression for a named method.

For example: someStream.forEach(e -> { System.out.println(e)});

or: someStream.forEach(System.out::println);

someStream.toArray(size -> new T[size]);

or: someStream.toArray(T[]::new);

Optional• A container object which may or may not contain a

non-null value. If a value is present, isPresent() will return true and get() will return the value.

• Additional methods that depend on the presence or absence of a contained value are provided, such as orElse() (return a default value if value not present) and ifPresent() (execute a block of code if the value is present).

Spliterator• An object for traversing and partitioning elements of a

source.

• A spliterator is the parallel analogue of an Iterator; it describes a (possibly infinite) collection of elements, with support for sequentially advancing, bulk traversal, and splitting off some portion of the input into another spliterator which can be processed in parallel. At the lowest level, all streams are driven by a spliterator. (from java.util.streams)

Creating Streams…Must have a Source! Streams have no data of their own:

• Collections (Collection interface) List<T> myList = …; myList.stream()…; myList.parallelStream() …;

• Arrays: T[] myArray = …; Arrays.stream(myArray)… // alternatively … Stream.of(myArray)…

…Creating Streams…Retrofitted JDK classes:

• BufferedReader.lines() — lines of a file

• Files.lines(Path file) — lines of a file

• Files.list(Path dir) — file paths contained in dir

• Random.ints()

• BitSet.stream()

• Pattern.splitAsStream(java.lang.CharSequence)

• JarFile.stream()

…Creating Streams…• Individual Values:

T t1; T t2; T t3; Stream.of(t1, t2, t3)…

• Stream.Builder Stream.Builder<String> sb = Stream.builder(); sb.accept(s1); sb.accept(s2); Stream<String> s = sb.build(). …; or Stream<String> s = Stream.<String>builder().add(s1).add(s2).build();

Creating Streams…• String: String myName… // produces an IntStream of code points String.chars()…

• Generator functions: //Infinite unordered Stream Stream.generate(Supplier<T> s) Stream.iterate(T seed, UnaryOperator<T> f)

Now that you have a Stream, what do you do with it?

• Transform

• Aggregate

• Find

• Reduce

I'm bored. Where's the code?

(from State of the Lambda: Libraries Edition — slightly edited)

[The following] is a fragment from the JDK class, Class (the getEnclosingMethod method), which loops over all declared methods, matching method name, return type, and number and type of parameters. …

First, the Original Codefor (Method m : enclosingInfo.getEnclosingClass().getDeclaredMethods()){ if (m.getName().equals(enclosingInfo.getName()) ) { Class<?>[] candidateParamClasses = m.getParameterTypes(); if (candidateParamClasses.length == parameterClasses.length) { boolean matches = true; for (int i = 0; i < candidateParamClasses.length; i++) { if (!candidateParamClasses[i].equals(parameterClasses[i])) { matches = false; break; } }

if (matches) { // finally, check return type if (m.getReturnType().equals(returnType)) return m; } } } }

throw new InternalError("Enclosing method not found");

Then, the Streamified codereturn Arrays.stream(enclosingInfo.getEnclosingClass().getDeclaredMethods()) .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName()) .filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses)) .filter(m -> Objects.equals(m.getReturnType(), returnType)) .findFirst() .orElseThrow(() -> new InternalError("Enclosing method not found");

Intermediate Operations• map

• filter

• limit

• skip

• parallel

• sequential

— documented in java.util.stream.Stream

map (mapToInt, mapToDouble, flatMap)• Stream<R> map(Function<? super T,? extends R> mapper)

• The mapper function is applied to each element of the stream and returns a Stream consisting of the results.

• Example: Stream<String> shaggyLines = … List<String> trimmedLines = shaggyLines.map(String::trim) .collect(toList);

• Example2: See Grep.java for flatmap

filter• Stream<T> filter(Predicate<? super T> predicate)

• Returns a stream consisting of the elements of this stream that match the given predicate.

• Example: IntStream.range(1, 100)

.filter( e -> ((e % 5 == 0) || (e % 3 == 0)) ) .mapToObj( e -> { return

(e % 15 == 0) ? "FizzBuzz" : (e % 5 == 0) ? "Buzz" : "Fizz" ; } )

.forEach(System.out::println);

parallel & sequential• parallel() — returns an equivalent stream that is parallel,

maybe

• sequential() - returns an equivalent stream that is sequential

• Example: IntStream.range(0, 100) .parallel(). …

limit & skip• limit(long maxSize) — limits a stream to this number of

elements

• skip(long count) — skips over that many elements

• Example: Stream.iterate(2, n -> n + 2) .limit(10) .skip(4) .forEach(System.out::println);

Terminal Operations• forEach

• toArray, toList

• reduce

• collect

• min, max, count, sum

• anyMatch, allMatch, noneMatch

• findFirst, findAny

• iterator, spliterator

findFirst & findAny• short-circuit operations

• both return an Optional

• Example in Streamified Class code

collect• The argument to collect() is a Collector, which embodies a recipe for folding

elements into a data structure or summary.

• The following will accumulate strings into an ArrayList: List<String> asList = stringStream.collect(Collectors.toList());

• The following will classify Person objects by city: Map<String, List<Person>> peopleByCity = personStream.collect(Collectors.groupingBy(Person::getCity));

• The following will classify Person objects by state and city, cascading two Collectors together:

Map<String, Map<String, List<Person>>> peopleByStateAndCity = personStream.collect(Collectors.groupingBy(Person::getState, Collectors.groupingBy(Person::getCity)));

Read these!State of the Lambda

September 2013 http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html

State of the Lambda: Libraries Edition September, 2013

http://cr.openjdk.java.net/~briangoetz/lambda/lambda-libraries-final.html

Package description for java.util.stream Java™ Platform Standard Ed. 8

http://docs.oracle.com/javase/8/docs/api/index.html

References• Marty Hall's Java 8 Tutorials:

http://www.coreservlets.com/java-8-tutorial/

• Faster parallel processing in Java using Streams and a spliterator, Marko Topolnik, PhD. :

https://www.airpair.com/java/posts/parallel-processing-of-io-based-data-with-java-streams

• Processing Data with Java SE 8 Streams, Part 1,Raoul-Gabriel Urma:

http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html

“You can know the name of a bird in all the languages of the world, but when you're finished,

you'll know absolutely nothing whatever about the bird… So let's look at the bird and see what it's doing — that's what counts. I learned very

early the difference between knowing the name of something and knowing something.”

— Richard Feynman

Recommended