stream api: рекомендации лучших собаководов

68
Stream API: рекомендации лучших собаководов Тагир Валеев Институт систем информатики СО РАН 1

Upload: tvaleev

Post on 15-Apr-2017

1.993 views

Category:

Software


6 download

TRANSCRIPT

Page 1: Stream API: рекомендации лучших собаководов

1

Stream API: рекомендации лучших

собаководовТагир Валеев

Институт систем информатики СО РАН

Page 2: Stream API: рекомендации лучших собаководов

2

Что это за чувак на сцене?https://github.com/amaembo/streamex

Page 3: Stream API: рекомендации лучших собаководов

3

Что это за чувак на сцене?• JDK-8072727 Add variation of Stream.iterate() that's finite• JDK-8136686 Collectors.counting can use Collectors.summingLong to reduce boxing• JDK-8141630 Specification of Collections.synchronized* need to state traversal constraints• JDK-8145007 Pattern splitAsStream is not late binding as required by the specification• JDK-8146218 Add LocalDate.datesUntil method producing Stream<LocalDate>• JDK-8147505 BaseStream.onClose() should not allow registering new handlers after stream is consumed• JDK-8148115 Stream.findFirst for unordered source optimization• JDK-8148250 Stream.limit() parallel tasks with ordered non-SUBSIZED source should short-circuit• JDK-8148838 Stream.flatMap(...).spliterator() cannot properly split after tryAdvance()

Page 4: Stream API: рекомендации лучших собаководов

4

Что это за чувак на сцене?

Page 5: Stream API: рекомендации лучших собаководов

5

Page 6: Stream API: рекомендации лучших собаководов

6

source.stream()

Page 7: Stream API: рекомендации лучших собаководов

7

.map(x -> x.squash())

Page 8: Stream API: рекомендации лучших собаководов

8

.filter(x -> x.getColor() != YELLOW)

Page 9: Stream API: рекомендации лучших собаководов

9

.forEach(System.out::println)

Page 10: Stream API: рекомендации лучших собаководов

10

source.stream() .map(x -> x.squash()) .filter(x -> x.getColor() != YELLOW) .forEach(System.out::println)

Page 11: Stream API: рекомендации лучших собаководов

11

AtomicLong cnt = new AtomicLong();

.peek(x -> cnt.incrementAndGet())

Page 12: Stream API: рекомендации лучших собаководов

12

AtomicLong cnt = new AtomicLong();

source.stream() .map(x -> x.squash()) .peek(x -> cnt.incrementAndGet()) .filter(x -> x.getColor() != YELLOW) .forEach(System.out::println)

Page 13: Stream API: рекомендации лучших собаководов

13

Источники(source)

Переходы(intermediate operation)

Сливы(terminal operation)

Page 14: Stream API: рекомендации лучших собаководов

14

Источники

Page 15: Stream API: рекомендации лучших собаководов

15

Задача №1:Сделать источник дочерних узлов для заданного родительского DOM XML узла.

Page 16: Stream API: рекомендации лучших собаководов

16

Задача №1:Сделать источник дочерних узлов для заданного родительского DOM XML узла.

public static Stream<Node> children(Node parent) { NodeList nodeList = parent.getChildNodes(); return IntStream.range(0, nodeList.getLength()) .mapToObj(nodeList::item);}

Page 17: Stream API: рекомендации лучших собаководов

17

Задача №1:Сделать источник дочерних узлов для заданного родительского DOM XML узла.

public static Stream<Node> children(Node parent) { NodeList nodeList = parent.getChildNodes(); return IntStream.range(0, nodeList.getLength()) .mapToObj(nodeList::item);}

Random.ints()Random.longs()String.chars()Files.lines()Files.list()

LocalDate.datesUntil()Process.descendants()ProcessHandle.allProcesses()NetworkInterface.inetAddresses()NetworkInterface.subInterfaces()

Page 18: Stream API: рекомендации лучших собаководов

18

Задача №2:Сделать источник элементов списка вместе с индексами.

public class IndexedValue<T> { public final int index; public final T value;

public IndexedValue(int index, T value) { this.index = index; this.value = value; }}

public static <T> Stream<IndexedValue<T>> withIndices(List<T> list) { return IntStream.range(0, list.size()) .mapToObj(idx -> new IndexedValue<>(idx, list.get(idx)));}

Page 19: Stream API: рекомендации лучших собаководов

19

Задача №3:Сделать источник пользователей в существующем классе Group.

public class Group { private User[] users; public Stream<User> users() { return Arrays.stream(users); }}

public class Group { private Map<String, User> nameToUser; public Stream<User> users() { return nameToUser.values().stream(); }}

public class Group { private List<User> users; public Stream<User> users() { return users.stream(); }}

Page 20: Stream API: рекомендации лучших собаководов

20

Задача №4:Сделать источник, генерирующий декартово произведение списков строк.

List<List<String>> input = asList(asList("a", "b", "c"), asList("x", "y"), asList("1", "2", "3"));>> ax1>> ax2>> ax3>> ay1>> ay2...>> cy2>> cy3

Page 21: Stream API: рекомендации лучших собаководов

21

Задача №4:Сделать источник, генерирующий декартово произведение списков строк.

List<List<String>> input = asList(asList("a", "b", "c"), asList("x", "y"), asList("1", "2", "3"));

Stream<String> stream = input.get(0).stream().flatMap(a -> input.get(1).stream().flatMap(b -> input.get(2).stream().map(c -> a + b + c)));stream.forEach(System.out::println);>> ax1>> ax2>> ax3>> ay1...

Page 22: Stream API: рекомендации лучших собаководов

22

Задача №4:Сделать источник, генерирующий декартово произведение списков строк.

List<List<String>> input = asList(asList("a", "b", "c"), asList("x", "y"), asList("1", "2", "3"));

Supplier<Stream<String>> s = input.stream() // Stream<List<String>> .<Supplier<Stream<String>>>map(list -> list::stream) // Stream<Supplier<Stream<String>>> .reduce((sup1, sup2) -> () -> sup1.get() .flatMap(e1 -> sup2.get().map(e2 -> e1+e2))) // Optional<Supplier<Stream<String>>> .orElse(() -> Stream.of(""));

s.get().forEach(System.out::println);

Page 23: Stream API: рекомендации лучших собаководов

23

Промежуточныеоперации

Page 24: Stream API: рекомендации лучших собаководов

24

Задача №5:Выбрать из стрима все элементы заданного класса.

IntStream.range(0, nodeList.getLength()) .mapToObj(nodeList::item) .???

Page 25: Stream API: рекомендации лучших собаководов

25

Задача №5:Выбрать из стрима все элементы заданного класса.

IntStream.range(0, nodeList.getLength()) .mapToObj(nodeList::item) .filter(node -> node instanceof Element);

IntStream.range(0, nodeList.getLength()) .mapToObj(nodeList::item) .filter(Element.class::isInstance);

Page 26: Stream API: рекомендации лучших собаководов

26

Задача №5:Выбрать из стрима все элементы заданного класса.

IntStream.range(0, nodeList.getLength()) .mapToObj(nodeList::item) .filter(node -> node instanceof Element) .map(node -> (Element)node);

IntStream.range(0, nodeList.getLength()) .mapToObj(nodeList::item) .filter(Element.class::isInstance) .map(Element.class::cast);

Page 27: Stream API: рекомендации лучших собаководов

27

Задача №5:Выбрать из стрима все элементы заданного класса.

public static <T, TT> Stream<TT> select(Stream<T> stream, Class<TT> clazz) { return stream.filter(clazz::isInstance).map(clazz::cast);}

select(IntStream .range(0, nodeList.getLength()) .mapToObj(nodeList::item), Element.class) .forEach(e -> System.out.println(e.getTagName()));

Page 28: Stream API: рекомендации лучших собаководов

28

Page 29: Stream API: рекомендации лучших собаководов

29

Задача №5:Выбрать из стрима все элементы заданного класса.

public static <T, TT> Function<T, Stream<TT>> select(Class<TT> clazz) { return e -> clazz.isInstance(e) ? Stream.of(clazz.cast(e)) : null;}

IntStream.range(0, nodeList.getLength()) .mapToObj(nodeList::item) .flatMap(select(Element.class)) .forEach(e -> System.out.println(e.getTagName()));

Page 30: Stream API: рекомендации лучших собаководов

30

Задача №5:Выбрать из стрима все элементы заданного класса.

IntStreamEx.range(nodeList.getLength()) .mapToObj(nodeList::item) .select(Element.class) .forEach(e -> System.out.println(e.getTagName()));

Page 31: Stream API: рекомендации лучших собаководов

31

Задача №6:Оставить значения, которые повторяются не менее n раз.

List<String> list = Arrays.asList("JPoint", "Joker", "JBreak", "JBreak", "Joker", "JBreak");

Map<String, Long> counts = list.stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));counts.values().removeIf(cnt -> cnt < 3);counts.keySet().forEach(System.out::println);>> JBreak

Page 32: Stream API: рекомендации лучших собаководов

32

Задача №6:Оставить значения, которые повторяются не менее n раз.

public static <T> Predicate<T> distinct(long atLeast) { Map<T, Long> map = new ConcurrentHashMap<>(); return t -> map.merge(t, 1L, Long::sum) == atLeast;}

List<String> list = Arrays.asList("JPoint", "Joker", "JBreak", "JBreak", "Joker", "JBreak");

list.stream().filter(distinct(3)).forEach(System.out::println);>> JBreak

Page 33: Stream API: рекомендации лучших собаководов

33

Задача №7:Реализовать takeWhile: прервать Stream по условию

public static <T> Predicate<T> takeWhile(Predicate<T> predicate) { AtomicBoolean matched = new AtomicBoolean(); return t -> { if(matched.get()) return false; if(!predicate.test(t)) { matched.set(true); return false; } return true; };}

Stream.of(1, 2, 3, -1, 4, 5, 6).filter(takeWhile(x -> x > 0)) .forEach(System.out::println);

Page 34: Stream API: рекомендации лучших собаководов

34

Задача №7:Реализовать takeWhile: прервать Stream по условию

public static <T> Stream<T> takeWhile(Stream<T> stream, Predicate<T> predicate) { Spliterator<T> src = stream.spliterator(); Spliterator<T> res = new Spliterators.AbstractSpliterator<T>(src.estimateSize(), src.characteristics() & ~Spliterator.SIZED & ~Spliterator.SUBSIZED) { boolean finished = false; T next;

@Override public boolean tryAdvance(Consumer<? super T> action) { if (finished || !src.tryAdvance(t -> next = t) || !predicate.test(next)) { finished = true; return false; } action.accept(next); return true; } }; return StreamSupport.stream(res, stream.isParallel()).onClose(stream::close);}

Page 35: Stream API: рекомендации лучших собаководов

35

Задача №7:Реализовать takeWhile: прервать Stream по условию

takeWhile(new Random().ints().boxed(), x -> x % 10 != 0) .forEach(System.out::println);

IntStreamEx.of(new Random()) .boxed() .takeWhile(x -> x % 10 != 0) .forEach(System.out::println);

Page 36: Stream API: рекомендации лучших собаководов

36

Терминальныеоперации

Page 37: Stream API: рекомендации лучших собаководов

37

Коллекторы

Page 38: Stream API: рекомендации лучших собаководов

38

Коллекторы

Page 39: Stream API: рекомендации лучших собаководов

39

Коллекторыpublic interface Collector<T, A, R> { Supplier<A> supplier();

BiConsumer<A, T> accumulator();

BinaryOperator<A> combiner();

Function<A, R> finisher();

Set<Characteristics> characteristics();}

Page 40: Stream API: рекомендации лучших собаководов

40

Задача №8:Преобразовать List<Map<K, V>> в Map<K, List<V>>

Дано: [{a=1, b=2}, {a=3, d=4, e=5}, {a=5, b=6, e=7} ]

Надо: {a=[1, 3, 5], b=[2, 6], d=[4], e=[5, 7] }

Page 41: Stream API: рекомендации лучших собаководов

41

Задача №8:Преобразовать List<Map<K, V>> в Map<K, List<V>>

input.stream() .flatMap(map -> map.entrySet().stream()) .collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));

Page 42: Stream API: рекомендации лучших собаководов

42

Задача №8:Преобразовать List<Map<K, V>> в Map<K, List<V>>

input.stream() .flatMap(map -> map.entrySet().stream()) .collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));

StreamEx.of(input) .flatMapToEntry(m -> m) .grouping();

Page 43: Stream API: рекомендации лучших собаководов

43

Задача №8:Преобразовать List<Map<K, V>> в Map<K, List<V>>

import static java.util.stream.Collectors.*;

input.stream() .flatMap(map -> map.entrySet().stream()) .collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toList())));

Page 44: Stream API: рекомендации лучших собаководов

44

Стримоз (сущ., тж. стримомания) — патологическое импульсивно возникающее стремление разместить всю логику Java-программы или существенной её части в одном Stream-конвейере, сопровождающееся непреодолимой потребностью удовлетворить это стремление.

Page 45: Stream API: рекомендации лучших собаководов

45

Задача №9:Найти отделы с суммарной зарплатой всех сотрудников болеемиллиона рублей, отсортировав по убыванию суммарной зарплаты.

interface Employee { Department getDepartment(); long getSalary();}

Page 46: Stream API: рекомендации лучших собаководов

46

Задача №9:Найти отделы с суммарной зарплатой всех сотрудников болеемиллиона рублей, отсортировав по убыванию суммарной зарплаты.

Map<Department, Long> deptSalaries = employees.stream().collect( groupingBy(Employee::getDepartment, summingLong(Employee::getSalary)));

deptSalaries.entrySet().stream() .filter(e -> e.getValue() > 1_000_000) .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new));

Page 47: Stream API: рекомендации лучших собаководов

47

Задача №9:Найти отделы с суммарной зарплатой всех сотрудников болеемиллиона рублей, отсортировав по убыванию суммарной зарплаты.

Map<Department, Long> deptSalaries = StreamEx.of(employees) .groupingBy(Employee::getDepartment, summingLong(Employee::getSalary));

EntryStream.of(deptSalaries) .filterValues(salary -> salary > 1_000_000) .reverseSorted(Map.Entry.comparingByValue()) .toCustomMap(LinkedHashMap::new);

Page 48: Stream API: рекомендации лучших собаководов

48

Задача №9:Найти отделы с суммарной зарплатой всех сотрудников болеемиллиона рублей, отсортировав по убыванию суммарной зарплаты.

Map<Department, Long> result = employees.stream().collect( collectingAndThen(groupingBy(Employee::getDepartment, summingLong(Employee::getSalary)), deptSalaries -> deptSalaries.entrySet().stream() .filter(e -> e.getValue() > 1_000_000) .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new))));

Не делайте так никогда!

Page 49: Stream API: рекомендации лучших собаководов

49

Задача №10:Найти все максимальные элементы по заданному компаратору.

Stream.of("JBreak", "JPoint", "Joker") .collect(maxAll(Comparator.comparing(String::length)));>> [JBreak, JPoint]

Page 50: Stream API: рекомендации лучших собаководов

50

Задача №10:Найти все максимальные элементы по заданному компаратору.

public static <T> Collector<T, ?, List<T>> maxAll(Comparator<T> cmp) { BiConsumer<List<T>, T> accumulator = (list, t) -> { if (!list.isEmpty()) { int c = cmp.compare(list.get(0), t); if (c > 0) return; if (c < 0) list.clear(); } list.add(t); }; return Collector.of(ArrayList::new, accumulator, (l1, l2) -> { l2.forEach(t -> accumulator.accept(l1, t)); return l1; });}

Page 51: Stream API: рекомендации лучших собаководов

51

Задача №11:Проверить, уникальны ли входные элементы (да или нет).

Stream.of("JUG.ru", "JBreak", "JPoint", "Joker", "JFocus", "JavaOne", "JBreak") .allMatch(ConcurrentHashMap.newKeySet()::add);

Page 52: Stream API: рекомендации лучших собаководов

52

Задача №12:Посчитать хэш-код строки стандартным алгоритмом:

"JBreak".hashCode();>> -2111961387"JBreak".chars().reduce(0, (a, b) -> a*31+b);>> -2111961387

Page 53: Stream API: рекомендации лучших собаководов

53

Задача №12:Посчитать хэш-код строки стандартным алгоритмом:

"JBreak".hashCode();>> -2111961387"JBreak".chars().reduce(0, (a, b) -> a*31+b);>> -2111961387"JBreak".chars().parallel().reduce(0, (a, b) -> a*31+b);>> 4473889

Page 54: Stream API: рекомендации лучших собаководов

54

Задача №12:Посчитать хэш-код строки стандартным алгоритмом:

"JBreak".hashCode();>> -2111961387"JBreak".chars().reduce(0, (a, b) -> a*31+b);>> -2111961387"JBreak".chars().parallel().reduce(0, (a, b) -> a*31+b);>> 4473889

(a*31+b)*31+c != a*31+(b*31+c)

Page 55: Stream API: рекомендации лучших собаководов

55

Задача №12:Посчитать хэш-код строки стандартным алгоритмом:

public static int foldLeft(IntStream input, int seed, IntBinaryOperator op) { int[] box = { seed }; input.forEachOrdered(t -> box[0] = op.applyAsInt(box[0], t)); return box[0];}

foldLeft("JBreak".chars().parallel(), 0, (a, b) -> a*31+b);

Page 56: Stream API: рекомендации лучших собаководов

56

Любым способом

Page 57: Stream API: рекомендации лучших собаководов

57

Задача №13:Обработать перекрывающиеся пары из входного потока

List<String> input = Arrays.asList("A", "B", "C", "D", "E");

StreamEx.of(input) .pairMap((a, b) -> a + " -> " + b) .toList();>> [A -> B, B -> C, C -> D, D -> E]

Page 58: Stream API: рекомендации лучших собаководов

58

Задача №13:Обработать перекрывающиеся пары из входного потока

List<String> input = Arrays.asList("A", "B", "C", "D", "E");

IntStream.range(0, input.size()-1) .mapToObj(idx -> input.get(idx)+" -> "+input.get(idx+1)) .collect(Collectors.toList());>> [A -> B, B -> C, C -> D, D -> E]

Page 59: Stream API: рекомендации лучших собаководов

59

Задача №13:Обработать перекрывающиеся пары из входного потока

List<String> input = Arrays.asList("A", "B", "C", "D", "E");

public static <T, R> Stream<R> pairs(List<T> input, BiFunction<T, T, R> mapper) { return IntStream.range(0, input.size()-1) .mapToObj(idx -> mapper.apply(input.get(idx), input.get(idx+1)));}

pairs(input, (a, b) -> a + " -> " + b) .collect(Collectors.toList());>> [A -> B, B -> C, C -> D, D -> E]

Page 60: Stream API: рекомендации лучших собаководов

60

Задача №13:Обработать перекрывающиеся пары из входного потока

List<String> input = Arrays.asList("A", "B", "C", "D", "E");

List<String> result = new ArrayList<>();input.stream().reduce((a, b) -> { result.add(a+" -> "+b); return b;});>> [A -> B, B -> C, C -> D, D -> E]

Не делайте так никогда!

Page 61: Stream API: рекомендации лучших собаководов

61

Задача №13:Обработать перекрывающиеся пары из входного потока

List<String> input = Arrays.asList("A", "B", "C", "D", "E");

String s = input.stream().map(a -> a+";"+a) .collect(Collectors.joining(" -> "));>> A;A -> B;B -> C;C -> D;D -> E;EPattern.compile(";").splitAsStream(s) .filter(b -> b.contains(" -> ")) .collect(Collectors.toList());>> [A -> B, B -> C, C -> D, D -> E]

Не делайте так никогда!

Page 62: Stream API: рекомендации лучших собаководов

62

Задача №13:Обработать перекрывающиеся пары из входного потока

static <T, TT, A, R> Collector<T, ?, R> pairs(BiFunction<T, T, TT> mapper, Collector<TT, A, R> downstream) { class Acc { T first, last; A acc = downstream.supplier().get(); void add(T next) { if(last == null) first = next; else downstream.accumulator().accept(acc, mapper.apply(last, next)); last = next; } Acc combine(Acc other) { downstream.accumulator().accept(acc, mapper.apply(last, other.first)); acc = downstream.combiner().apply(acc, other.acc); last = other.last; return this; } } return Collector.of(Acc::new, Acc::add, Acc::combine, acc -> downstream.finisher().apply(acc.acc));}

Page 63: Stream API: рекомендации лучших собаководов

63

Задача №14:Разбить входной поток на группы равной длины

List<String> input = Arrays.asList ("a", "b", "c", "d", "e", "f", "g", "h");

>> [[a, b, c], [d, e, f], [g, h]]

Page 64: Stream API: рекомендации лучших собаководов

64

Задача №14:Разбить входной поток на группы равной длины

static <T> Stream<List<T>> ofSublists(List<T> list, int n) { int size = list.size(); return IntStream.rangeClosed(0, (size-1) / n).mapToObj(idx -> list.subList(idx * n, Math.min(size, idx * n + n)));}

List<String> input = Arrays.asList ("a", "b", "c", "d", "e", "f", "g", "h");

List<List<String>> list = ofSublists(input, 3) .map(ArrayList::new) .collect(Collectors.toList());>> [[a, b, c], [d, e, f], [g, h]]

Page 65: Stream API: рекомендации лучших собаководов

65

Задача №15:Разбить входной поток на группы, начинающиеся с данного элемента

List<String> input = Arrays.asList("Start", "a", "b", "c", "Start", "d", "Start", "e", "f", "g", "h", "i", "Start");

StreamEx.of(input) .groupRuns((a, b) -> !b.equals("Start")).toList();>> [[Start, a, b, c], [Start, d], [Start, e, f, g, h, i], [Start]]

Page 66: Stream API: рекомендации лучших собаководов

66

Задача №15:Разбить входной поток на группы, начинающиеся с данного элемента

List<String> input = Arrays.asList("Start", "a", "b", "c", "Start", "d", "Start", "e", "f", "g", "h", "i", "Start");

IntStream.rangeClosed(0, input.size()) .filter(idx -> idx == 0 || idx == input.size() || input.get(idx).equals("Start")) .boxed().collect(pairs(input::subList, Collectors.toList()));>> [[Start, a, b, c], [Start, d], [Start, e, f, g, h, i], [Start]]

Page 67: Stream API: рекомендации лучших собаководов

67

Библиотеки•jOOλ (by Lukas Eder)•https://github.com/jOOQ/jOOL

•Cyclops-react (by John McClean)•https://github.com/aol/cyclops-react

•Guava (FluentIterable)•https://github.com/google/guava

•javaslang (by Daniel Dietrich)•https://github.com/javaslang/javaslang

Page 68: Stream API: рекомендации лучших собаководов

68

Спасибо за вниманиеhttps://twitter.com/tagir_valeev

https://github.com/amaembo

https://habrahabr.ru/users/lany