Slide navigation: Forward with space bar, → arrow key, or PgDn. Backwards with ← or PgUp.
Copyright © Cay S. Horstmann 2016
List<String> words = ...; int count = 0; for (String w : words) { if (w.length() > 12) count++; }
long count = words.stream() .filter(w -> w.length() > 12) .count();
import java.util.stream.*; import java.nio.file.*; Stream<String> words = Files.lines(Paths.get("/usr/share/dict/words")); long count = words.filter(w -> w.length() > 12).count();
filter
) or a result (count
).Stream<T> stream = collection.stream();
Stream<T> stream = Stream.of(array);
Stream.of
is a varargs method:
Stream<String> song = Stream.of("gently", "down", "the", "stream");
Stream.empty
makes an empty stream:Stream<String> silence = Stream.empty();
Stream<String> echos = Stream.generate(() -> "Echo"); Stream<Double> randoms = Stream.generate(Math::random);
Stream.iterate
.f
that is iteratively applied:
Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));
seed
, f(seed)
, f(f(seed))
, and so on.Stream<String> lines = Files.lines(path);
try
-with-resources to make sure file is closed:
try (Stream<String> lines = Files.lines(path)) { Process lines }
String contents = ...; Pattern pattern = Pattern.compile("\\s+"); // Break at white space Stream<String> words = pattern.splitAsStream(contents);
Scanner.tokens, Matcher.results, ServiceLoader.stream, LocalDate.datesUntil, StackWalker.walk, ClassLoader.resources, Process.children/descendants, Catalog.catalogs, DriverManager.drivers
Scanner.tokens
gets a stream of tokens, similar to Pattern.splitAsStream
from Java 8:
Stream<String> tokens = new Scanner(path).useDelimiter("\\s*,\\s*").tokens();
BigInteger limit = new BigInteger("10000000"); Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.compareTo(limit) < 0, n -> n.add(BigInteger.ONE));
filter
yields a new stream:
List<String> words = ...; Stream<String> longWords = words.stream().filter(w -> w.length() > 12);
Predicate<T>
map
to transform all stream elements:
Stream<String> lowercaseWords = words.stream().map(String::toLowerCase); Stream<String> firstLetters = words.stream().map(s -> s.substring(0, 1));
flatMap
map
takes a Function<T, U>
and yields a Stream<U>
Stream
:
public static Stream<String> letters(String s) //letters("boat")
is aStream
["b", "o", "a", "t"]
letters
to a Stream<String>
, you get a Stream<Stream<String>>
:
[... ["y", "o", "u", "r"], ["b", "o", "a", "t"], ...]
Stream<String>
, use flatMap
:
Stream<String> flatResult = words.stream().flatMap(w -> letters(w))
// Yields [... "y", "o", "u", "r", "b", "o", "a", "t",
...]
Stream
-valued functions.
flatMap
to compose Optional
-valued functionslimit
returns a new stream that ends after a given number of elements (or earlier if the stream is shorter):
Stream<Double> randoms = Stream.generate(Math::random).limit(100);
skip
does the opposite:
Stream<String> words = Stream.of(contents.split("\\PL+")).skip(1);
concat
concatenates two streams:
Stream<String> combined = Stream.concat(letters("Hello"), letters("World"));
// Yields the stream ["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"]
distinct
suppresses duplicates (not necessarily adjacent):
Stream<String> uniqueWords
= Stream.of("merrily", "merrily", "merrily", "gently").distinct();
// Only one "merrily"
is retained
sorted
yields a stream of the elements in sorted order:
Stream<String> longestFirst = words.stream().sorted(Comparator.comparing(String::length).reversed());
peek
yields a stream with the same elements at the original, calling a function on each retrieved element:
Object[] powers = Stream.iterate(1.0, p -> p * 2) .peek(e -> System.out.println("Fetching " + e)) .limit(20).toArray();
long count = words.stream().filter(w -> w.length() > 12).count();
max
/min
methods return the largest or smallest stream element:
Optional<String> largest = words.max(String::compareToIgnoreCase);
Optional
because the stream might have been empty—see next slide.findFirst
yields the first element:
Optional<String> startsWithQ = words.filter(s -> s.startsWith("Q")).findFirst();
findAny
instead on a parallel stream.anyMatch
yields true
if a predicate has a match:
boolean aWordStartsWithQ = words.anyMatch(s -> s.startsWith("Q"));
allMatch
and noneMatch
.import java.util.stream.*; import java.nio.file.*; Stream<String> words() throws IOException { return Files.lines(Paths.get("/usr/share/dict/words")); } words().filter(w -> w.length() > 12).max(String::compareToIgnoreCase); words().filter(s -> s.startsWith("Qu")).findFirst();
Optional
TypeOptional<T>
wraps an object of type T
or no object.null
”.String result = optionalString.orElse(""); String result = optionalString.orElseGet(() -> System.getProperty("user.dir")); String result = optionalString.orElseThrow(IllegalStateException::new);
optionalValue.ifPresent(results::add); Optional<Boolean> added = optionalValue.map(results::add);
get
:
Optional<T> optionalValue = ...; optionalValue.get().someMethod()
T value = ...; value.someMethod(); // NPE ifvalue
isnull
if (optionalValue.isPresent()) optionalValue.get().someMethod();
if (value != null) value.someMethod();
Optional
, produce a wrapped value if there is a result:
public static Optional<Double> inverse(Double x) {
if (x != 0) return Optional.of
(1 / x);
Optional.empty()
if there is no result:
else return Optional.empty(); }
ofNullable
turns “object or null
” into an Optional
:
obj = find(queryData); return Optional.ofNullable(obj); //Optional.of(obj)
ifobj
not null, orOptional.empty()
flatMap
f
yields an Optional<T>
and T
has a method g
yielding an Optional<U>
.f
and g
by calling s.f().g()
? f
returns an Optional<T>
, not a T
.flatMap
instead:
Optional<U> result = s.f().flatMap(T::g);
s.f()
is Optional.of(t)
and t.g()
is Optional(r)
, that's the result.s.f()
is Optional.of(t)
and t.g()
is empty, the result is empty.s.f()
is empty, the result is empty.flatMap
on streams if you think of an Optional
as a stream of size zero or one.HttpClient.authenticator/cookieManager/proxy/sslParameters, ServiceLoader.findFirst, Runtime.Version.build/pre/post, ProcessHandle.of, ProcessHandle.Info.command/commandLine/arguments/
startInstant/totalCpuDuration/user
Optional
class.optionalValue.ifPresentOrElse(System.out::println, // Consumer () -> Runtime.exec("rm -rf /home")); // Runnable
Optional<Double> result = squareRoot(x).or(
() -> inverse(x)) // Another Optional
, supplied lazily
squareRoot(x)
nonempty? If so, it is the answer.inverse(x)
. Optional
Java 9 NewsStream<T> stream()Yields a stream of length 0 or 1.
Stream.flatMap
to drop empty results:
class Person { public Optional<String> nickname() // Not everyone has one ... } Stream<String> nicknames = people.map(Person::nickname) .flatMap(Optional::stream);
String contents = optionalString.orElseThrow();
get
Optional
Cheat Sheetdata = opt.orElse(defaultValue); data = opt.orElseGet(() -> ...); data = opt.orElseThrow(SomeException::new); data = opt.orElseThrow(); // same asget
, throwsNoSuchElementException
, Java 10
opt.ifPresent(value -> ...); opt.ifPresentOrElse(value -> ..., () -> ...); // Java 9
anotherOpt = opt.filter(value -> ...).map(value -> ...).or(() -> ...);
Optional
Code SmellsOptional
should never be null
!Optional
type.
null
is manageable inside a class.Optional
type.
Optional
intended as return type.List
of Optional
values or a Map
with Optional
keys is dubious.
List<Optional<String>>
, just have List<String>
Optional
to make a null
check:
Optional.ofNullable(result).ifPresentOrElse(...); // Way too complex
Iterator<T> iter = stream.iterate()
stream.forEach(System.out::println);
toArray
yields an array of stream elements.String[] result = stream.toArray(String[]::new); //stream.toArray()
has typeObject[]
collect
method passes stream elements to a Collector
.Collectors
has many factory methods for collectors:
List<String> result = stream.collect(Collectors.toList()); Set<String> result = stream.collect(Collectors.toSet());
TreeSet<String> result = stream.collect(Collectors.toCollection(TreeSet::new));
String result = stream.collect(Collectors.joining(", "));
summarizingInt
, summarizingLong
, summarizingDouble
yield object with methods sum
, average
, max
, min
:
IntSummaryStatistics summary = stream.collect(Collectors.summarizingInt(String::length)); double averageWordLength = summary.getAverage(); int maxWordLength = summary.getMax();
Collectors.toMap
:
Map<Integer, String> idToName = people.collect( Collectors.toMap(Person::getId, Person::getName));
Map<Integer, Person> idToPerson = people.collect( Collectors.toMap(Person::getId, Function.identity()));
Stream<Locale> locales = Stream.of(Locale.getAvailableLocales()); Map<String, String> languageNames = locales.collect( Collectors.toMap(Locale::getCountry, Locale::getDisplayLanguage, (existingValue, newValue) -> existingValue));
Map<String, List<Locale>> countryToLocales = locales.collect( Collectors.groupingBy(Locale::getCountry));
List<Locale> swissLocales = countryToLocales.get("CH");
// Yields list of locales [it_CH, de_CH, fr_CH, ...]
partitioningBy
when the classifier function has a boolean
result:
Map<Boolean, List<Locale>> englishAndOtherLocales = locales.collect( Collectors.partitioningBy(l -> l.getLanguage().equals("en"))); List<Locale>> englishLocales = englishAndOtherLocales.get(true);
Stream<Locale> locales() { return Stream.of(Locale.getAvailableLocales()); }
locales().collect(Collectors.toMap(Locale::toString, Locale::getDisplayLanguage))
locales().collect(Collectors.toMap(Locale::getCountry, Locale::getDisplayLanguage))
// Exception—duplicate keys
Map<String, List<Locale>> countryToLocales = locales().collect(
Collectors.groupingBy(Locale::getCountry));
countryToLocales.get("CH")
// See http://archive.ethnologue.com/15/show_language.asp?code=gsw
groupingBy
produces a list for each key.Map<String, Set<Locale>> countryToLocaleSet = locales.collect( groupingBy(Locale::getCountry, toSet()));
java.util.stream.Collectors.*
to make the expressions easier to read.Map<String, Long> countryToLocaleCounts = locales.collect( groupingBy(Locale::getCountry, counting()));
Map<String, Integer> stateToCityPopulation = cities.collect( groupingBy(City::getState, summingInt(City::getPopulation)));
Map<String, Set<String>> countryToLanguages = locales.collect( groupingBy(Locale::getDisplayCountry, mapping(Locale::getDisplayLanguage, toSet())));
filtering
, flatMapping
:
Map<String, Set<City>> largeCitiesByState = cities.collect( groupingBy(City::getState, filtering(c -> c.getPopulation() > 500000, toSet())));
cities.filter(...).collect(...)
? toUnmodifiableList
, toUnmodifiableSet
, toUnmodifiableMap
reduce
is a general mechanism for computing a value from a stream.List<Integer> values = ...; Optional<Integer> sum = values.stream().reduce((x, y) -> x + y);
List<Integer> values = ...; Integer sum = values.stream().reduce(0, (x, y) -> x + y) // Computes 0 + v0 + v1 + ...
Optional
.reduce
for more complex operations.(Int, String) -> Int
.int result = words.reduce(0, (total, word) -> total + word.length(), (total1, total2) -> total1 + total2);
map(String::length)
and compute the sum.Stream<T>
, where T
is a class (or interface).Stream<Integer>
, Stream<Double>
, etc. are inefficient.IntStream
, LongStream
, DoubleStream
instead.OptionalInt
, OptionalLong
, OptionalDouble
get
, call getAsInt
, getAsLong
, and getAsDouble
IntStream stream = IntStream.of(1, 1, 2, 3, 5); stream = Arrays.stream(values, from, to);
IntStream
and LongStream
ranges:
IntStream zeroToNinetyNine = IntStream.range(0, 100); LongStream zeroToTenBillion = LongStream.rangeClosed(0, 10_000_000_000L);
Random.ints
, Random.longs
, Random.doubles
String sentence = "\uD835\uDD46 is the set of octonions.";
IntStream codes = sentence.codePoints();
// A stream with elements 0x1D546 0x20 0x69 0x73 0x20
. . .
mapToInt
, mapToLong
, mapToDouble
:
IntStream lengths = words.mapToInt(String::length);
toArray
method returns int[]
, long[]
, double[]
OptionalInt
etc.sum
, average
, max
, min
summaryStatistics
yields IntSummaryStatistics
etc. which simultaneously provide all four results.boxed
yields an object stream:
Stream<Integer> integers = IntStream.range(0, 100).boxed();
IntStream.range(0, 10).map(n -> n * n).forEach(System.out::println) new Random().ints().limit(1000).toArray() String hello = "Hello \uD83D\uDE3B"; Arrays.toString(hello.codePoints().toArray()) new Random().ints().map(n -> n % 100).limit(1000).max() new Random().ints().map(n -> n % 100).limit(1000).summaryStatistics()
Stream<String> parallelWords = words.parallelStream(); Stream<String> parallelWords = Stream.of(wordArray).parallel();
int[] shortWords = new int[12]; words.parallelStream().forEach( s -> { if (s.length() < 12) shortWords[s.length()]++; }); // Error—race condition! System.out.println(Arrays.toString(shortWords));
Map<Integer, Long> shortWordCounts = words.parallelStream() .filter(s -> s.length() < 12) .collect(groupingBy(String::length, counting()));
Stream.sorted
are ordered.stream.map(fun)
can be done in segments that are reassembled in order.distinct
, limit
) benefit from dropping ordering:
Stream<String> sample = words.parallelStream().unordered().distinct().limit(n);
Collectors.groupingByConcurrent
Map<Integer, List<String>> result = words.parallelStream().collect( Collectors.groupingByConcurrent(String::length)); // Values aren’t collected in stream order
Files.lines
now uses a memory mapped file.Files.lines(pathOfHugeFile) .parallel() .filter(l -> l.contains("Exception")) ...