Upload
tvaleev
View
1.985
Download
6
Embed Size (px)
Citation preview
1
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()
4
Что это за чувак на сцене?
5
6
source.stream()
7
.map(x -> x.squash())
8
.filter(x -> x.getColor() != YELLOW)
9
.forEach(System.out::println)
10
source.stream() .map(x -> x.squash()) .filter(x -> x.getColor() != YELLOW) .forEach(System.out::println)
11
AtomicLong cnt = new AtomicLong();
.peek(x -> cnt.incrementAndGet())
12
AtomicLong cnt = new AtomicLong();
source.stream() .map(x -> x.squash()) .peek(x -> cnt.incrementAndGet()) .filter(x -> x.getColor() != YELLOW) .forEach(System.out::println)
13
Источники(source)
Переходы(intermediate operation)
Сливы(terminal operation)
14
Источники
15
Задача №1:Сделать источник дочерних узлов для заданного родительского DOM XML узла.
16
Задача №1:Сделать источник дочерних узлов для заданного родительского DOM XML узла.
public static Stream<Node> children(Node parent) { NodeList nodeList = parent.getChildNodes(); return IntStream.range(0, nodeList.getLength()) .mapToObj(nodeList::item);}
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()
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)));}
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(); }}
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
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...
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);
23
Промежуточныеоперации
24
Задача №5:Выбрать из стрима все элементы заданного класса.
IntStream.range(0, nodeList.getLength()) .mapToObj(nodeList::item) .???
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);
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);
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()));
28
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()));
30
Задача №5:Выбрать из стрима все элементы заданного класса.
IntStreamEx.range(nodeList.getLength()) .mapToObj(nodeList::item) .select(Element.class) .forEach(e -> System.out.println(e.getTagName()));
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
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
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);
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);}
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);
36
Терминальныеоперации
37
Коллекторы
38
Коллекторы
39
Коллекторыpublic interface Collector<T, A, R> { Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics();}
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] }
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())));
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();
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())));
44
Стримоз (сущ., тж. стримомания) — патологическое импульсивно возникающее стремление разместить всю логику Java-программы или существенной её части в одном Stream-конвейере, сопровождающееся непреодолимой потребностью удовлетворить это стремление.
45
Задача №9:Найти отделы с суммарной зарплатой всех сотрудников болеемиллиона рублей, отсортировав по убыванию суммарной зарплаты.
interface Employee { Department getDepartment(); long getSalary();}
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));
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);
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))));
Не делайте так никогда!
49
Задача №10:Найти все максимальные элементы по заданному компаратору.
Stream.of("JBreak", "JPoint", "Joker") .collect(maxAll(Comparator.comparing(String::length)));>> [JBreak, JPoint]
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; });}
51
Задача №11:Проверить, уникальны ли входные элементы (да или нет).
Stream.of("JUG.ru", "JBreak", "JPoint", "Joker", "JFocus", "JavaOne", "JBreak") .allMatch(ConcurrentHashMap.newKeySet()::add);
52
Задача №12:Посчитать хэш-код строки стандартным алгоритмом:
"JBreak".hashCode();>> -2111961387"JBreak".chars().reduce(0, (a, b) -> a*31+b);>> -2111961387
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
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)
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);
56
Любым способом
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]
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]
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]
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]
Не делайте так никогда!
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]
Не делайте так никогда!
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));}
63
Задача №14:Разбить входной поток на группы равной длины
List<String> input = Arrays.asList ("a", "b", "c", "d", "e", "f", "g", "h");
>> [[a, b, c], [d, e, f], [g, h]]
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]]
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]]
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]]
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
68
Спасибо за вниманиеhttps://twitter.com/tagir_valeev
https://github.com/amaembo
https://habrahabr.ru/users/lany