2. Что это за чувак на сцене?
https://github.com/amaembo/streamex
2
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()
3
16. Задача №1:
Сделать источник дочерних узлов для заданного родительского
DOM XML узла.
public static Stream<Node> children(Node parent) {
NodeList nodeList = parent.getChildNodes();
return IntStream.range(0, nodeList.getLength())
.mapToObj(nodeList::item);
}
16
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()
17
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)));
}
18
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();
}
}
19
29. Задача №5:
Выбрать из стрима все элементы заданного класса.
29
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:
Выбрать из стрима все элементы заданного класса.
30
IntStreamEx.range(nodeList.getLength())
.mapToObj(nodeList::item)
.select(Element.class)
.forEach(e -> System.out.println(e.getTagName()));
31. Задача №6:
Оставить значения, которые повторяются не менее n раз.
31
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 раз.
32
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 по условию
33
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 по условию
34
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 по условию
35
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);
41. Задача №8:
Преобразовать List<Map<K, V>> в Map<K, List<V>>
41
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>>
42
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>>
43
import static java.util.stream.Collectors.*;
input.stream()
.flatMap(map -> map.entrySet().stream())
.collect(groupingBy(Map.Entry::getKey,
mapping(Map.Entry::getValue,
toList())));
44. 44
Стримоз (сущ., тж. стримомания) — патологическое
импульсивно возникающее стремление разместить всю
логику Java-программы или существенной её части в одном
Stream-конвейере, сопровождающееся непреодолимой
потребностью удовлетворить это стремление.
45. Задача №9:
Найти отделы с суммарной зарплатой всех сотрудников более
миллиона рублей, отсортировав по убыванию суммарной зарплаты.
45
interface Employee {
Department getDepartment();
long getSalary();
}
46. Задача №9:
Найти отделы с суммарной зарплатой всех сотрудников более
миллиона рублей, отсортировав по убыванию суммарной зарплаты.
46
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:
Найти отделы с суммарной зарплатой всех сотрудников более
миллиона рублей, отсортировав по убыванию суммарной зарплаты.
47
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:
Найти отделы с суммарной зарплатой всех сотрудников более
миллиона рублей, отсортировав по убыванию суммарной зарплаты.
48
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:
Найти все максимальные элементы по заданному компаратору.
49
Stream.of("JBreak", "JPoint", "Joker")
.collect(maxAll(Comparator.comparing(String::length)));
>> [JBreak, JPoint]
50. Задача №10:
Найти все максимальные элементы по заданному компаратору.
50
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:
Проверить, уникальны ли входные элементы (да или нет).
51
Stream.of("JUG.ru", "JBreak", "JPoint",
"Joker", "JFocus", "JavaOne", "JBreak")
.allMatch(ConcurrentHashMap.newKeySet()::add);
52. Задача №12:
Посчитать хэш-код строки стандартным алгоритмом:
52
"JBreak".hashCode();
>> -2111961387
"JBreak".chars().reduce(0, (a, b) -> a*31+b);
>> -2111961387
53. Задача №12:
Посчитать хэш-код строки стандартным алгоритмом:
53
"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:
Посчитать хэш-код строки стандартным алгоритмом:
54
"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:
Посчитать хэш-код строки стандартным алгоритмом:
55
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);
57. Задача №13:
Обработать перекрывающиеся пары из входного потока
57
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:
Обработать перекрывающиеся пары из входного потока
58
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:
Обработать перекрывающиеся пары из входного потока
59
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:
Обработать перекрывающиеся пары из входного потока
60
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:
Обработать перекрывающиеся пары из входного потока
61
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;E
Pattern.compile(";").splitAsStream(s)
.filter(b -> b.contains(" -> "))
.collect(Collectors.toList());
>> [A -> B, B -> C, C -> D, D -> E]
Не делайте так никогда!
62. Задача №13:
Обработать перекрывающиеся пары из входного потока
62
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:
Разбить входной поток на группы равной длины
63
List<String> input = Arrays.asList
("a", "b", "c", "d", "e", "f", "g", "h");
>> [[a, b, c], [d, e, f], [g, h]]
64. Задача №14:
Разбить входной поток на группы равной длины
64
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:
Разбить входной поток на группы, начинающиеся с данного элемента
65
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:
Разбить входной поток на группы, начинающиеся с данного элемента
66
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. Библиотеки
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