Introduction of Java 8 with emphasis on Lambda Expressions and Streams
Java 8 Streams
-
Upload
bjoern-kimminich -
Category
Technology
-
view
2.994 -
download
3
description
Transcript of Java 8 Streams
Java 8 Streams
J8S 1.2.1 (10.04.2023)
Björn Kimminich
https://twitter.com/bkimminichhttps://linkedin.com/in/bkimminichhttps://google.com/+BjörnKimminichhttp://slideshare.net/BjrnKimminich/
Agenda
•Java 8•Streams•Lambdas•Method
References
Introduction
•Intermediate vs. Terminal
•Stateless vs. Stateful
•Short-Circuiting
•Collectors
Stream Operations
Java 8
Java™ SE 8 Release Contents
JSR 308: Annotations on Java TypesJSR 310: Date and Time APIJSR 335: Lambda Expressions
closuresJEP 107: Bulk Data Operations for Collections
for-eachfiltermapreduce http://www.jcp.org/en/jsr/detail?id=337
http://openjdk.java.net/jeps/107
Streams
Streams
interface java.util.stream.Stream<T>forEach()filter()map()reduce()…
java.util.Collection<T>Stream<T> stream()Stream<T> parallelStream()
Creating and using a Stream
List<Book> myBooks = …;
Stream<Book> books = myBooks.stream();
Stream<Book> goodBooks =books.filter(b -> b.getStarRating() > 3);
goodBooks.forEach(b -> System.out.println(b.toString()));
Properties of Streams
Streams do not store elements……they are a view on top of a data structure
Operations provided by Streams...…are applied to the underlying data source elements
Stream Operations can take as a parameter…
…Lambda expressions…Method references
Manipulating the underlying data source...
…will yield a ConcurrentModificationException
Lambdas
What are Lambda Expressions?
Java‘s version of ClosuresAllow to create (sort of) First Class Functions
Can be passed as a parameterCan be a return valueCan be stored in a variable
Target type is inferred from context
Why „sort of“?Because Java doesn‘t have a real function type!Java converts Lambdas into Functional Interfaces
Functional Interface
Functional Interface = Interface w/ 1 MethodNames of Interface and Method are irrelevantDefault: java.util.function.Consumer<T>
public interface Stream<T> { void forEach(Consumer<? super T> consumer); } public interface Consumer<T> {void accept(T t);}
Consumer<Book> reduceRankForBadAuthors = (Book b) -> { if (b.getStarRating() < 2) b.getAuthor().addRank(-1); };
books.forEach(reduceRankForBadAuthors);
books.forEach(b -> b.setEstimatedReadingTime(90*b.getPages()));
Lambda Syntax
/* argument list */ (int x, int y) -> { return x*y; } (x, y) -> { return x*y; } x -> { return x*2; }
() -> { System.out.println("Do you think this will work?"); }
/* single expression */ b -> { b.getMissingPages() > threshold ? b.setCondition(BAD)
: b.setCondition(GOOD) }
/* list of statements */ b -> { Condition c = computeCondition(b.getMissingPages()); b.setCondition(c); }
Method References
Method References
books.forEach(b -> b.fixSpellingErrors()); books.forEach(Book::fixSpellingErrors); // instance method
books.forEach(b -> BookStore.generateISBN(b)); books.forEach(BookStore::generateISBN); // static method
books.forEach(b -> System.out.println(b.toString())); books.forEach(System.out::println); // expression
Stream<ISBN> isbns1 = books.map(b -> new ISBN(b)); Stream<ISBN> isbns2 = books.map(ISBN::new); // constructor
Intermediate vs. TerminalStream Operations
Intermediate vs. Terminal
Intermediate: Output is another Stream
filter()map()…
Terminal: Do something else with the Stream
forEach()reduce()…double totalPrice = books.mapToDouble(Book::getPrice)
.reduce(0.0, (p1, p2) -> p1+p2);
Stream Evaluation
Intermediate Streams are not evaluated…
…until a Terminal Operation is invoked on them
Intermediate = LazyTerminal = Eager (Consuming)
This allows Java to……do some code optimization during compilation…avoid buffering intermediate Streams…handle parallel Streams more easily
Terminal = Consuming Operations
books.forEach(b -> System.out.println("Book: " + b.getTitle())); double totalPrice = books.reduce(0.0, (b1, b2)
-> b1.getPrice() + b2.getPrice());
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
Intermediate Operations can be chainedOnly one Terminal Operation can be invokedBest avoid reference variables to Streams entirely by using Fluent Programming
Construction (Intermediate)* Terminal;
Stateless vs. StatefulStream Operations
Stateless Intermediate Operations
Operation need nothing other than the current Stream element to perform its workExamples
map() Maps element to something elsefilter() Apply predicate and keep or drop elementList<Book> myBooks = ...;
double impairments = myBooks.stream().filter(b -> b.getCondition().equals(BAD))
.mapToDouble(Book::getPrice) .reduce(0.0, (p1, p2) -> p1 + p2);
Stateful Intermediate Operations
Operations that require not only the current stream element but also additional state
distinct() Element goes to next stage if it appears the first timesorted() Sort elements into natural ordersorted(Comparator) Sort according to provided Comparatorsubstream(long) Discard elements up to provided offsetsubstream(long, long) Keep only elements in between offsetslimit(long) Discard any elements after the provided max. size
myBooks.stream().map(Book::getAuthor).distinct().forEach(System.out::println);
Short-CircuitingStream Operations
Short-Circuiting Operations
Processing might stop before the last element of the Stream is reached
Intermediatelimit(long)substream(long, long)
TerminalanyMatch(Predicate)allMatch(Predicate)noneMatch(Predicate)findFirst()findAny()
Author rp = new Author("Rosamunde Pilcher");
boolean phew = myBooks.stream() .map(Book::getAuthor) .noneMatch(isEqual(rp));
System.out.println("Am I safe? " + phew);
Collectors
Collectors
<R> R collect(Collector<? super T, A, R> col)
Collect the elements of a Stream into some other data structurePowerful and complex toolCollector is not so easy to implement, but…
…luckily there are lots of factory methods for everyday use in java.util.stream.Collectors
toList()toSet()toCollection(Supplier)toMap(Function, Function)…
Collector Examples
List<Author> authors = myBooks.stream().map(Book::getAuthor)
.collect(Collectors.toList());
double averagePages = myBooks.stream()
.collect(Collectors.averagingInt(Book::getPages));
Joining Collector
Used for concatenation of CharSequencesInternally implemented using StringBuilder
A lot more efficient than a Map-Reduce with intermediately concatenated Strings
// not efficient due to recursive String concatenation. And ugly. String titleList = myBooks.stream().map(Book::getTitle).reduce("", (t1, t2) -> t1+t2);
// Still inefficient. Still ugly (initial line break)titleList = myBooks.stream().map(Book::getTitle).reduce("", (t1, t2) -> t1+"\n"+t2);
// more efficient thanks to StringBuilder. Pretty printed. titleList = myBooks.stream().map(Book::getTitle).collect(Collectors.joining("\n"));
Thank you…
…for your attention!
All code examples: https://github.com/bkimminich/java8-streams