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 Конрада Гаруса в блоге Белки . |