The Collectors.reducing factory method is a generalization of all of them. (link)
IntSummaryStatistics menuStatistics = menu.stream().collect(summarizingInt(Dish::getCalories (link)
double avgCalories = menu.stream().collect(averagingInt(Dish::getCalories)); (link)
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories)); (link)
Comparator dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories); Optional mostCalorieDish = menu.stream() .collect(maxBy(dishCaloriesComparator)); (link)
Vikram: [Demo of Collectors.maxBy. It takes a comparator which is produced using comparingInt, which in turn takes a lambda that returns an int.]
long howManyDishes = menu.stream().collect(Collectors.counting()); (link)
Vikram: [Demonstrates the counting method.]
the groupingBy recipe says, “Make a Map whose keys are (currency) buckets and whose values are a list of elements in those buckets.” (link)
Map<Currency, List> transactionsByCurrencies = transactions.stream().collect(groupingBy(Transaction::getCurrency)); (link)
Vikram: [In functional programming we specify what and not how. The above takes several lines of code in regular style of programming.]
Function<Integer, Apple> c2 = Apple::new; 1 Apple a2 = c2.apply(110); 2 (link)
Comparator describes a function descriptor with the signature (T, T) -> int. (link)
Note that none of the functional interfaces allow for a checked exception to be thrown. (link)
Vikram: [define your own if you need it]
To refresh a little: every Java type is either a reference type (for example, Byte, Integer, Object, List) or a primitive type (for example, int, double, byte, char). But generic parameters (for example, the T in Consumer) can be bound only to reference types. (link)
Vikram: [A primitives cannot take the place of a generic parameter without some automatic operation. This automatic operation is called autoboxing.]
The signature of the abstract method of the functional interface describes the signature of the lambda expression. We call this abstract method a function descriptor. (link)
Vikram: [functional descriptor is the signature such as ()-{}]
Java 8 also added a specialized version of the functional interfaces we described earlier in order to avoid autoboxing operations when the inputs or outputs are primitives. (link)
Vikram: [This means that there is a IntPredicate to test primitive ints with signature (int i) -> boolean. similarly there are specialized interfaces for all primitives.]
and map are two separate operations, they were merged into the same pass (compiler experts call this technique loop fusion). (link)
Stream operations that can be connected are called intermediate operations, and operations that close a stream are called terminal operations. (link)
Streams library, by contrast, uses internal iteration (link)
stream as a set of values spread out in time. In contrast, a collection is a set of values spread out in space (link)
Note that, similarly to iterators, a stream can be traversed only once. (link)
By contrast, a stream is a conceptually fixed data structure (you can’t add or remove elements from it) whose elements are computed on demand (link)
A collection is an in-memory data structure that holds all the values the data structure currently has—every element in the collection has to be computed before it can be added to the collection. (link)
Internal iteration— In contrast to collections, which are iterated explicitly using an iterator, stream operations do the iteration behind the scenes for you. (link)
First, what exactly is a stream? A short definition is “a sequence of elements from a source that supports data-processing operations.” (link)
File[] hiddenFiles = new File(".").listFiles(File::isHidden); (link)
And this means using parallel processing—something Java wasn’t previously friendly to (link)
It gives you a new concise way to express behavior parameterization (link)
Java 8 adds the ability to pass methods (your code) as arguments to other methods. Figure 1.3, based on figure 1.2, illustrates this idea. We also refer to this conceptually as behavior parameterization. (link)
Vikram: [lambdas]
Its evolution (via the addition of new features) from Java 1.1 (1997) to Java 7 (2011) has been well managed. Java 8 was released in March 2014, Java 9 in September 2017, Java 10 in March 2018, and Java 11 planned for September 2018. (link)
Vikram: [1.1 in 97, 8 in 2014, 9 in 2017, 10 in 2018]
Multicore processors aren’t fully served by pre-Java-8 programming practice. (link)
Vikram: [Because, the only way to do it was by using Threads and Synchronization which was error prone ]
Java 8 facilitates new programming styles to better exploit such computers. (link)
Vikram: [Java 8 Streams allows programmers to exploit multiple cores. It provides functions to process streams of data. If these functions do not modify state, these can be executed in parallel.]
Java 8 added default methods to support evolvable interfaces. (link)
Vikram: [Before Java 8, whenever a new method was added to an Interface, all programs that used that interface had to be recompiled. Now, in Java 8, a default implementation can be provided so the implementing classes will not have to be recompiled.]
Java 8 introduced the Optional class that, if used consistently, can help you avoid null-pointer exceptions. (link)
Vikram: [To avoid null-pointer exceptions, use Optional class]
The key motivation for this is that you can now program in Java 8 at a higher level of abstraction, structuring your thoughts of turning a stream of this into a stream of that (similar to how you think when writing database queries) rather than one item at a time. Another advantage is that Java 8 can transparently run your pipeline of Stream operations on several CPU cores on disjoint parts of the input—this is parallelism almost for free instead of hard work using Threads. (link)
These techniques not only have the benefit of being cheaper than threads, but also have a major advantage from developers’ point of view: they raise the level of abstraction of implementing concurrent and asynchronous applications, allowing developers to concentrate on the business requirements instead of dealing with typical problems of low-level multithreading issues such as synchronization, race conditions, and deadlocks. (link)
Vikram: [reactive techniques]
The Reactive Manifesto (https://www.reactivemanifesto.org)—developed in 2013 and 2014 by Jonas Bonér, Dave Farley, Roland Kuhn, and Martin Thompson—formalized a set of core principles for developing reactive applications and systems. (link)
Reactive programming addresses these issues by allowing you to process and combine streams of data items coming from different systems and sources in an asynchronous way (link)
Vikram: [The issues are : big data, expectation of faster response times, diversity of environments]
We quickly realized that by combining our energy and diverse backgrounds we could deliver, not just a short book on Java 8 lambdas, but instead a book that, we hope, the Java community will still be reading in five or ten years. (link)
Vikram: [a book written for the long term and hopefully concentrates on concepts]
But since Java 8 you can use a lambda expression, so the call to Thread would look like this:
Thread t = new Thread(() -> System.out.println("Hello world")); (link)
Vikram: [()->{}]
the behavior of the filterApples method depends on the code you pass to it via the ApplePredicate object. You’ve parameterized the behavior of the filterApples method! (link)
Vikram: [The code is passed as a lambda. A lambda to filter green apples, another to filter heavy apples etc.]
predicate (a function that returns a boolean) (link)
Vikram: [(T) -> boolean]
Behavior parameterization is a software development pattern that lets you handle frequent requirement changes. In a nutshell, it means taking a block of code and making it available without executing it. (link)
Vikram: [Similar to Ruby blocks. A block of code can be given to a method rather than just primitives and objects.]
Behavior parameterization is the ability for a method to take multiple different behaviors as parameters and use them internally to accomplish different behaviors. (link)
Callable interface is used to model a task that returns a result. (link)
The methods takeWhile and dropWhile are more efficient than a filter when you know that the source is sorted (link)
In comparison, our approach using iterate was purely immutable; you didn’t modify existing state but were creating new tuples at each iteration. (link)
Vikram: [The approach using generate uses state of the previous call and is therefore mutable.]
The generate method on IntStream takes an IntSupplier instead of a Supplier. For example, here’s how to generate an infinite stream of ones:
IntStream ones = IntStream.generate(() -> 1); (link)
Similarly to the method iterate, the method generate lets you produce an infinite stream of values computed on demand. But (link)
Vikram: [You have to supply a lambda that satisfies Supplier.]
The iterate method takes an initial value, here 0, and a lambda (of type Unary-Operator) to apply successively on each new value produced. (link)
For example, a useful method is Files.lines, which returns a stream of lines as strings from a given file. (link)
You can create a stream from an array using the static method Arrays.stream, which takes an array as parameter (link)
You can get an empty stream using the empty method as follows:
Stream emptyStream = Stream.empty(); (link)
You can create a stream with explicit values by using the static method Stream.of, which can take any number of parameters (link)
IntStream intStream = menu.stream().mapToInt(Dish::getCalories); 1 Stream stream = intStream.boxed(); 2 (link)
Vikram: [converting int stream back to general stream]
the map operation of an IntStream takes a lambda that takes an int and produces an int (an IntUnaryOperator). (link)
int calories = menu.stream() 1 .mapToInt(Dish::getCalories) 2 .sum(); (link)
Vikram: [mapToInt returns specialised int stream.]
Using the flatMap method has the effect of mapping each array not with a stream but with the contents of that stream. (link)
In a nutshell, the flatMap method lets you replace each value of a stream with another stream and then concatenates all the generated streams into a single stream. (link)
Arrays.stream()that takes an array and produces a stream: (link)
that limit(n) and skip(n) are complementary (link)
Streams support the limit(n) method, which returns another stream that’s no longer than a given size. (link)
it even works if there are an infinite number of remaining elements! (link)
Vikram: [referring to dropWhile]
The dropWhile operation is the complement of takeWhile (link)
The takeWhile operation is here to rescue you! It lets you slice any stream (even an infinite stream as you will learn later) using a predicate. (link)
Java 9 added two new methods that are useful for efficiently selecting elements in a stream: takeWhile and dropWhile. (link)
Streams API can work out several optimizations behind the scenes. In addition, using internal iteration, the Streams API can decide to run your code in parallel (link)
The second edition of this book, Modern Java in Action: Lambdas, Streams, Functional and Reactive Programming, is written to get you over that initial hump of “sounds good in principle, but it’s all a bit new and unfamiliar” and into coding like a native (link)
But Java 8 can optimize operations on streams in a way that Java can’t do for collections—for example, it can group together several operations on the same stream so that the data is traversed only once instead of expensively traversing it multiple times. Even better, Java can automatically parallelize stream operations for you (unlike collections). (link)
Vikram: [The benefit is that multiple operations can be done in a single pass and these can be parallelized.]
Thus, you can process streams that are too big to fit in your computer memory. (link)
Vikram: [One major use case where Stream processing is required.]