Streams API — это настоящая жемчужина в Java 8, и я продолжаю находить более или менее неожиданные способы их использования. Недавно я писал об их использовании в качестве фасада ForkJoinPool . Вот еще один интересный пример: ходячие рекурсивные структуры данных.
Без лишних слов взгляните на код:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
class Tree { private int value; private List<Tree> children = new LinkedList<>(); public Tree(int value, List<Tree> children) { super(); this.value = value; this.children.addAll(children); } public Tree(int value, Tree... children) { this(value, asList(children)); } public int getValue() { return value; } public List<Tree> getChildren() { return Collections.unmodifiableList(children); } public Stream<Tree> flattened() { return Stream.concat( Stream.of(this), children.stream().flatMap(Tree::flattened)); }} |
Это довольно скучно, за исключением нескольких выделенных строк.
Допустим, мы хотим иметь возможность найти элементы, соответствующие некоторым критериям в дереве, или найти определенный элемент. Один из типичных способов сделать это — рекурсивная функция, но она имеет некоторую сложность и, вероятно, потребует изменяемого аргумента (например, набора, в который вы можете добавлять соответствующие элементы). Другой подход — итерация со стеком или очередью. Они отлично работают, но занимают несколько строк кода и не так легко обобщить.
Вот что мы можем сделать с этой flattened функцией:
|
01
02
03
04
05
06
07
08
09
10
11
|
// Get all values in the tree:t.flattened().map(Tree::getValue).collect(toList());// Get even values:t.flattened().map(Tree::getValue).filter(v -> v % 2 == 0).collect(toList());// Sum of even values:t.flattened().map(Tree::getValue).filter(v -> v % 2 == 0).reduce((a, b) -> a + b);// Does it contain 13?t.flattened().anyMatch(t -> t.getValue() == 13); |
Я думаю, что это решение довольно гладкое и универсальное. Одной строки кода (здесь разделенной на 3 для удобства чтения в блоге) достаточно, чтобы сгладить дерево в простой поток, который можно искать, фильтровать и еще много чего.
Хотя он не идеален: он не ленив и flattened , каждый раз вызывается для каждого узла в дереве. Возможно, это можно улучшить с помощью Supplier . В любом случае, это не имеет значения для типичных, достаточно небольших деревьев, особенно в бизнес-приложениях на очень высоком стеке библиотек. Но для очень больших деревьев, очень частого выполнения и жестких временных ограничений могут возникнуть некоторые проблемы.
| Ссылка: | Ходьба Рекурсивные структуры данных с использованием Java 8 Streams от нашего партнера JCG Конрада Гаруса в блоге Белки . |