Stream API (шпаргалка по Хорстману)

  java8, Uncategorized, шпаргалка

Зачем нужны потоки ввода-вывода?
1) Удобно организовать параллелизм вычислений

Пример потока ввода-вывода
long count = words.stream().filter(w -> w.length() > 12).count();
Основные особенности потоков ввода-вывода Stream API:
1) В потоке не хранятся элементы, как в коллекции.
2) Потоки ввода-вывода не изменяют свой источник(коллекцию, массив, сроку и др.). Но они возвращают новые потоки, содержащие результат.
3) Операции с потоками ввода-вывода выполняются по требованию. Когда их результат требуется – тогда они и выполняются.
Можно организовать бесконечные потоки ввода-вывода.
4) конвеер потоковых операций создается в три этапа
а) создание потока – Stream(), generate(), iterate()
б) ряд промежуточных операций(одна, несколько или ни одной) filter(), map(), flatMap()
в) оконечная операция count (когда запрашивается этот метод начинается работа промежуточных методов, пока запрашиваемый результат не будет получен)

подсчет слов в параллельном потоке
long count = words.parallelStream().filter(w -> w.length() > 12).count();

Методы создания потоков

для обработки массива используем статический метод Stream.of
String[] sarr = {…};
Stream words = Stream.of(sarr);
или
Stream words = Stream.of(someText.split(“[\\P{L}]+”));
или
Stream song = Stream.of(“anykey”,”manykey”,”yellow”,”warranty”);

для создания пустого потока
Stream enjoyTheSilence = Stream.empty();

методы бесконечных потоков ввода-вывода

static generate(Supplier)

поток ввода-вывода констант
Stream echos = Stream.generate(() – > “Echo”);

поток ввода-вывода случайных чисел
Stream randoms = Stream.generate(Math::random);

static iterate(UnaryOperator)

Вывести последовательность 0 1 2 3 …
Stream integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));

Промежуточные методы

filter([boolean])

filter(boolean)
filter(Predicate)

List wordList = …;
Stream words = wordList.stream();
Stream longWords = words.filter(w -> w.length() > 12);

map()

преобразуем слова в слова с маленькой буквы
Stream lowcaseWords = somewords.map(String::toLowerCase);
получаем поток символов с первой буквой каждого слова
Stream firstCharacters = somewords.map(s -> s.charAt(0));

flatMap() – сводит результат

например, если есть метод выводящий поток:
public static Stream characterStream(String s) {
List result = new ArrayList();
for (char c : s.toCharArray()) result.add(c);
return result.stream();

Stream letters = words.map(w – > characterStream(w)) // выведет [[s][o][m][e],[s][e][n][t][e][n][c][e],[e][x][a][m][p][l][e]]
Stream letters = words.flatmap(w – > characterStream(w)) // выведет [[s][o][m][e][s][e][n][t][e][n][c][e][e][x][a][m][p][l][e]]

flatMap() – есть еще в других методах

limit(n)

Stream.limit(n) возвращает новый поток, ограниченный n-результатами
Stream randoms = Stream.generate(Math::random).limit(1000);//получить поток с 1000 случайными числами

skip(n)

Stream.skip(n) возвращает новый поток, пропуская первые n элементов

concat(Stream s1, Stream s2)

Stream.concat(stream1, stream2) // объединить два потока (только если первый поток не бесконечный, иначе нет смысла)

peek(someFunction)

функция в аргументе peek вызывается всякий раз при извлечении элемента(например, удобно при логировании)

distinct()

возвращает поток равнозначный исходному но без дубликатов

sorted()

возвращает отсортированный поток
есть вариант метода принимающий Соmparable а есть принимающий Comparator
Stream shortestFirst = words.sorted(Comparator.comparing(String::length))
особенно полезен как часть конвеера
Collections.sort – сортирует коллекцию на месте
Stream.sorted() – возвращает отсортированный поток

Оконечные(терминальные) методы

выполняют операции завершающие поток и представляют результаты
count() -оконечный метод (подсчет всех значений)
max()
min()
findAny()
Optional startsWithQ = words.parallel.filter(s -> s.startsWith(“Q”)).findAny(); //метод эффективен в распараллеливании, только находится вхождение – сразу возвращает результат

anyMatch() проверяет на наличие совпадения
allMatch() – возвращает boolean
noneMatch() – возвращает boolean

findFirst() (часто употребляется вместе с filter)

Все оконечные методы возвращают Optional
(этот специальный тип ввели чтобы не возвращать null)

Optional -оболочка ответа

Неправильное использование Optional

1)Optional optionalValue = …; //выбросит NoSuchElemantException
optionalValue.get().someMethod()
2)T value = …;
value.someMethod();

Правильное использование Optional, метод isPresent()

метод isPresent без аргументов сразу возвращает результат
метод isPresent с аргументами ничего не возвращает, но позволяет сразу обработать результат:
optVal.ifPresent(v -> somethingToDoWithV)
somethingToDoWithV- функция которая как-то обрабатывает значение, если поток что-то возвращает, если ничего не возвращает, то просто ничего не происходит

если есть v то добавить его
optionalValue.ifPresent(v -> results.add(v))
или
optionalValue.ifPresent(results::add)

метод Optional.map() для обработки резултата
(аналог Stream.map())
Optional added = optionalValue.map(results::add)

Предоставляем дефолтное значение при отсутствии значения
String result = optionalString.orElse(“”);
или рассчитываем это значение
String result = optionalString.orElseGet(() – > System.getProperty(“user.dir”));
или кидаем исключение
String result = optionalString.orElseThrow(NoSuchElementException::new)

Просто создать объект Optional
Optional.of(result)
Optional.empty()

public static Optional inverse(Double x) {
return x == 0 ? Optional.empty() : Optional.of(1 / x);
}

ofNullable()

Optional.offNullable(object)
возвращает Optional.of(object) если object не null
в противном случае возвращает Optional.empty()

если
Optional f()
а g() возвращает не T, а другой метод
Optional T.g()
то
Optional = s.f().flatMap(T::g);

Если соединить много методов в цепочку, завершающуюся методом flatMap()
flatMap() как бы сводит потоки в поток потоков
Double res = Optional.of(-4.0).flatMap(Someclass::smthToDoMethod).flatMap(Someclass::smthToDoMethod).flatMap(Someclass::smthToDoMethod)… etc

Методы сведения

Если нужно объединить потоки в какой-то результат, например получить сумму используются методы сведения

reduce()

Операция сведения элементов x1,x2,x3, …xn должна быть ассоциативной
Stream values = …;
Optional sum = values.reduce((x, y) -> x + y)

то же самое с элементом тождества по отношению к данной ассоуиативной операции, (здесь – 0-тождественный элемент)
Stream values =…;
Integer sum = values.reduce(0,(x, y) -> x + y)

можно объединять результаты сведений
int result = words.reduce(0, (total, words) -> total + word.length(), (total1, total2) -> total1 + total2);
равносильно – words.mapToInt(String::length).sum

или words.

Результаты

результаты могут быть числами, массивами, коллекциями или объектами типа StringBuilder и др.
Чтобы получить результирующий массив нужного типа
String[] res = words.toArray(String[]::new)

Чтобы получить HashSet нельзя использовать reduce() (метод reduce() предоставляет только одно значение тождества, а HashSet непотоекобезопасен)

надо использовать collect()

collect()

сигнатура
collect(поставщик, накопитель, объединитель)
поставщик – нужен для получения новых экземпляров целевого объекта(напр. конструктор HashSet)
накопитель – добавляет новый элемент к существующему(напр. add())
объединитель – объединяет два объекта в один (напр. addAll())

HashSet result = stream.collect(HashSet::new, HashSet::add, HashSet::addAll);

однако есть уже готовые класс Collectors и интерфейс Collector
с ними, удобно и просто делать так:

List result = stream.collect(Collectors.toList());

Set result = stream.collect(Collectors.toSet());

TreeSet result = stream.collect(Collectors::toCollection(TreeSet::new));

String result = stream.collect(Collectors.joining());

String result = stream.collect(Collectors.joining(“, “));

если к примеру в исходном потоке попадаются не только строки, понадобится сначала приведение всего к строкам
String result = stream.map(Object::toString).collect(Collectors.joining(“, “));

получение суммы, максимального и средних значений… – summarizing, average, max, min
IntSummaryStatistics summary = words.collect(
Collectors.summarizingInt(String::length));
double averageWordLength = summary.getAverage();
double maxWordLength = summary.getMax()

можно сразу выводить на экран форичем
stream.forEach(System.out::println);

(оконечные методы forEach() и forEachOrdered() – обход метоодов в произвольном и исходном порядке)

Накопить элементы в Map, Collect()

Map idToName = people.collect(Collectors.toMap(Person::getId, Person::getName));
Накопить элементы в Map, заполнив значения конкретными элементами
Map idToPerson = people.collect(Collectors.toMap(Person::getId, Function.identity()));

дубль ключа вызовет IllegalStateException

кроме toMap() есть toConcurrentMap – дает параллельную Map

Накопители

groupingBy() – группировка
Map> countryToLocales = locales.collect(Collectors.groupingBy(Locale::getCountry))
где Locale::getCountry – классификатор группирования

получаем карту, значения которой – списки
Map> countryToLocaleSet = locales.collect(groupingBy(Locale::getCountry, toSet()));
используя накопитель Collectors.toSet()

есть также groupingByConcurrent()

partitioningBy -разбивка

counting()
maxBy()
minBy()

summingInt() summingLong() summingDouble()
Map stateToCitiPopulation= cities.collect(
groupingBy(City::getState, summingInt(City::getPopulation)));
суммарное население штатов в потоке городов

mapping() (требует для себя еще одного накопителя)
Map> stateToLongestCityName= cities.collect(
groupingBy(City::getState,
mapping(City::getName,
maxBy(Comparator.comparing(String::length)))));
города группируются по штатам, в каждом штате названия городов максимальной длины

Параллелизм

!!! ответственность за безопасность паралеллизма в простых потоках – на программисте

!!!Важно не модифицировать исходную коллекцию при выполнении потоковой операции

!!!не забываем что ФИ Predicate возвращает Boolean

!!! объединение Map-ов недешево поэтому используется распараллеливание
Map> result = cities.parallel().collect(Collectors.groupingByConcurrent(City::getState));//порядка тут нет только накопление

специально отключить упорядочивание в параллел выполнении можно так
Stream sample = stream.parrallel.unordered().limit(n);

просто параллелим
Stream parallelWords = Stream.of(wordArray).parallel();

Еще

разбить CharSequence по регулярке
Stream words = Pattern.compile(“[\\P{L}]+”).splitAsStream(contents);

получить потоки случайных чисел
Random.ints()
Random.longs()
Random.doubles()

преобразуем поток примитивов в поток объектов с помощью boxed()
Stream integers = Integer.range(0, 100).boxed();
наоборот, объекты в примитивы
Stream words = …;
IntStream lengths = words.mapToInt(String::length)

получить поток всех строк в файле
Files.lines()
применять с try-catch с ресурсами:
try (Stream lines = Files.lines(path)){
… обработка lines
}
поток и файл будут закрыты после обработки

так же, про codePoints() chars()
OptionalInt, OptionalLong, OptionalDouble
IntStream, LongStream, DoubleStream

IntStream stream = IntStream.of(1,1,2,3,5);
stream = Arrays.stream(values, from , to);
результат будет в int[]

range() rangeClosed() – для формирования диапазонов

LEAVE A COMMENT