Table of contents
- Item 42: Prefer lambdas to anonymous class
- Item 43: Prefer method references to lambdas
- Item 44: Favour the use of standard functional interfaces
- Item 45: Use streams judiciously
- Item 46: Prefer side-effect-free functions in streams
- Item 47: Prefer Collection to Stream as a return type
- Item 48: Use caution when making streams parallel
In Java8, some of the things that are added:
- lambdas, functional interface, method references -> create function objects
- streams -> processing sequence of data elements
Item 42: Prefer lambdas to anonymous class
function types: interface
/abstract class
with single abstract
method.
function objects: instances of function types
Lambda expressions
- Limited to functional interfaces (only one method required to be implemented by function objects)
- Lambda expressions can be used to make instances of functional interfaces.
- Java infers type, sometimes explicitly required
- Use generic instead of raw types for type inference, else explicit and verbose code would be needed
- Use them only if they make the code clearer and easy to understand
- Can not obtain a reference of itself.
this
refers to enclosing instance
When to use Anonymous class
- For
abstract
classes - If functional object is large, lambda becomes difficult to understand
- Want
this
to refer to newly created instance - Functional type has multiple
abstract
methods
Lambdas and Anonymous class are not reliable for serialization and deserialization. Avoid them, use private static
nested class instead if possible.
Item 43: Prefer method references to lambdas
method references: even more concise way to create function objects than lambda.
Where method references are shorter and clearer, use them; where they aren’t, stick with lambdas.
Item 44: Favour the use of standard functional interfaces
If one of the standard functional interfaces does the job, you should generally use it in preference to a purpose-built functional interface.
These are some of the 43 functional interfaces, the rest are derivations of these.
Use custom functional interface, if:
- It will be commonly used and could benefit from a descriptive name.
- It has a strong contract associated with it.
- It would benefit from custom
default
methods. - Always annotate your functional interfaces with the
@FunctionalInterface
annotation.
Don’t write overloadings that take different functional interfaces in the same argument position like the submit()
method of ExecutorService
.
Item 45: Use streams judiciously
- The streams API was added in Java 8 to ease the task of performing bulk operations, sequentially or in parallel. It consist of source stream, >=0 intermediate operation and one terminal operation.
- Stream pipelines are evaluated lazily: evaluation doesn’t start until the terminal operation is invoked, and data elements that aren’t required in order to complete the terminal operation are never computed.
- The streams API is fluent: it is designed to allow all of the calls that comprise a pipeline to be chained into a single expression
- Overusing streams makes programs hard to read and maintain.
- Using helper methods is even more important for readability in stream pipelines than in iterative code
- Refactor existing code to use streams and use them in new code only where it makes sense to do so, resist the urge.
Use code block
- want to read and modify local variables in scope
return
from enclosing method,break
,continue
,throw
Use Streams
- uniform transformation of sequences
- filter sequences
- combine with a single operation or attribute
- search on basis of a criterion
Item 46: Prefer side-effect-free functions in streams
- Streams is based on a functional paradigm. To make the most out of it adopt both the paradigm and the API.
- Structure the code as a sequence of pure functions (use the output of the previous function as input and don’t use/modify local state).
- The
forEach
operation should be used mostly to report the result of a stream computation, not to perform the computation. - In order to use streams properly, we have to know about collectors. The most important collector factories are
toList
,toSet
,toMap
,groupingBy
, andjoining
.
Item 47: Prefer Collection to Stream as a return type
Initially collection interfaces like Collection
, List
, Set
, Iterator
and Arrays
were used.
The decision to choose was simple:
- Use
Collection
interfaces - If can’t implement
Collection
useIterator
- If primitive elements are involved and performance is a concern -> Arrays
Stream
added in Java8 complicated the matters a bit.
Stream
and Iterable
are not compatible with each other even though they have same iterator methods because Stream
doesn’t implement Iterable
. They can be interchanged by writing trivial adapters.
Collection
or an appropriate subtype is generally the best return type for a public
, sequence returning method as it would implement both Iterable
and Stream
.
Return a small enough sequence using ArrayList
or HashSet
but don’t store a large sequence to later return like a Collection
as it has Integer.MAX_VALUE
limit. Use Stream
or Iterable
for large sequences.
Item 48: Use caution when making streams parallel
Writing concurrent programs in Java keeps getting easier, but writing concurrent programs that are correct and fast is as difficult as it ever was.
Safety and liveness violations are a fact of life in concurrent programming, and parallel stream pipelines are no exception.
- Parallelizing a pipeline is unlikely to increase its performance if the source is from
Stream.iterate
, or the intermediate operationlimit
is used. Performance gains from parallelism are best on streams over
ArrayList
,HashMap
,HashSet
, andConcurrentHashMap
instances; arrays; int ranges; and long ranges because they can be accurately and cheaply split into subranges of any desired sizes and divide for parallel work. Stream use spliterator for this. The other reason is the good - to excellent locality of reference which is of much use in efficient parallel processing.The terminal operation should be of reducing nature (like
reduce
or providedmin
,max
,sum
andcount
) for faster results.collect
method isn’t good as combining streams is costly. All others are intermediate candidates.- The
Stream
specification places stringent requirements on the function objects likemap
,filter
and programmer supplied. Adhere to them before parallelising else safety and correct result is compromised. - Under the right circumstances, it is possible to achieve near-linear speedup in the number of processor cores simply by adding a parallel call to a stream pipeline. Always try thoroughly before choosing.
This is it!