Java SE 8 For the Really Impatient, Note 3
3,475 words in 22 minutes
Chapter 2 The Stream API
Simple Reductions
reductions: reduces the stream to a value.
terminal operation: the stream ceases to be usable.
List of reductions:
- count
- max
- min
- findFirst
- findAny
- anyMatch
- allMatch
- noneMatch
count
: returns the number of elements of the stream.
max
: returns the largest value.min
: returns the smallest value.
These methods return an Optional<T>
value that either wraps the answer or indicates that there is none(empty stream).
findFirst
: returns the first value in a nonempty collection. Useful when combined with filter
.
findAny
: returns any match. Effective when you parallelize the stream since the first match in any of the examined segments will complete the computation.
anyMatch
: just want to know there is a match. Takes a predicate argument.
allMatch
: return true if all elements match a predicate.noneMatch
: return true if no elements match a predicate.
These methods always examine the entire stream, but they still benefit from being run in parallel.
The Optional Type
Either a wrapper for an object of type T
or for no object.
Intended as a safer alternative than a reference of type T
that returns to an obejct or null
.
Working with Optional Values
get
: gets the wrapped element if it exists, or throw a NoSuchElementException
if it doesn’tisPresent
: reports whether an Optional<T>
object has a value
No easier than if (value != null) value.someMethod();
The key to using Optional
effectively is to use a method that either consumes the correct value or produces an alternative.ifPresent
: accepts a function. If the optional value exists, it is passed to that function. Otherwise, nothing happens.
No value is returned in ifPresent
. Instead, map
returns the result.
added
has one of 3 values: true or false wrapped into an Optional
, if optionalValue
was present, or an empty optional otherwise.
orElse
: sets a default when there is no match
|
|
|
|
Creating Optional Values
Optional.of(result)
and Optional.empty()
are static methods to create Optionals.
ofNullable
: a bridge from the use of null values to optional values. Returns Optional.of(obj)
if obj is not null, otherwise, Optional.empty()
Composing Optional Value Functions with flatMap
Suppose you have a method f
yielding an Optional<T>
, and the target type T
has a method g
yielding an Optional<U>
.
If they were normal methods, you could compose them by call s.f().g(). This doesn’t work for Optionals because s.f()
has type Optional<T>
, not T
.
If s.f()
is present, then g
is applied to it. Otherwise, an empty Optional<U>
is returned.
Chaining flatMap
If either the inverse
method or the squareRoot
returns Optional.empty()
, the result is empty.
The flatMap
method of Optional works in the same way if you consider an optional value to be a stream of size zero or one.
Reduction Operations
If you want to compute a sum, or combine the elements of a stream to a result in another way, use one of the reduce
methods.
The reduce
method below computes v0+v1+v2+…
The method returns an Optional
because there is no valid result if the stream is empty.
In general, if the reduce
method has a reduction operation op, the reduction yields v0 op v1 op v2 op …, where we write vi op vi+1 for the function call op(vi, vi+1)
The operation should be associative: it should not matter in which order you combine the elements.
Useful associative operations: sum, product, string concatenation, maximum, minimum, set union and intersection.
subtraction is not associative.
identity: e such that e op x = x. E.g, 0 is the identity for addition.
The identity value is returned if the stream is empty, and you no longer need to deal with the Optional
class.
Simple form of reduce
requires a function (T, T) -> T
, with the same types for the arguments and the result.
If you want to calculate all lengths in a stream of strings, you need to use accumulator.
Easier to map to a stream of numbers and use one of its methods to compute sum, max or min.
Collecting Results
Just want to look at the result.
iterator
: yields an old fashioned iterator that you can use to visit the elements.toArray
: get an array of the stream elements.
stream.toArray()
returns an Object[]
array. If you want an array of the correct type, pass in the array constructor:
collect
: takes 3 arguments
- A supplier to make new instances of the target object, e.g, a constructor for hash set
- An accumulator that adds an element to the target, e.g, an
add
method - A combiner that merges 2 objects into 1, such as
addAll
1HashSet<String> result = stream.collect(HashSet::new, HashSet::add, HashSet:addAll);
The target object need not to be a collection. It could be a StringBuilder
or an object that tracks a count and a sum.
Collector
: convenient interfaceCollectors
: class with factory methods for common collectors
Control which kind of set you get
Collect all strings in a stream by concatenating them
Add delimiter between elements
Convert non-string to string first
If you want to reduce the stream results to a sum, average, maximum, or minimum, use one of the methods summarizing(Int|Long|Double)
. These methods take a function that maps the stream objects to a number and yield a result of type (Int|Long|Double)SummaryStatistics
, with methods for obtaining the sum, average, maximum and minimum.
forEach
: just want to print them or put them in a database
On a parallel stream, you must ensure that the function can be executed concurrently.
forEachOrdered
: execute in stream order. No parallelism benefits anymore.
forEach
and forEachOrdered
are terminal operations. If you want to continue using the stream, use peek
instead.
Collecting into Maps
Suppose you want to collect the elements in Stream<Person>
into a map so that you can later look up people by their id.Collectors.toMap
: has 2 function arguments that produce the map keys and values
Funtion.identity()
: values should be the actual elements
If there is more than one element with the same key, the collector will throw an IllegalStateException
. Supply a third function argument that determines the value for the key, given the existing and the new value.
Construct a map that contains, for each language in the available locales, as key its name in your default locale(such as “German”), and as value its localized name(such as “Deutsch”).
Suppose we want to know all languages in a given country, we need a Map<String, Set<String>>
If you want a TreeMap
, then you supply the constructor as the 4th argument.
For each of the toMap
methods, there is an equivalent toConcurrentMap
method that yields a concurrent map. A single concurrent map is used in the parallel collection process. When used with a parallel stream, a shared map is more efficient than merging maps, but of course, you give up ordering.