Есть Stream<String>
, как разбить его на несколько Stream или List, относительно тех строк, которые начинаются с определенного слова (аналогично split("word")
на обычной строке)?
Например, для "word":
[word, hello, dude, word and word2, stackoverflow, question, ask, word, example]
=>
[[hello, dude], [stackoverflow, question, ask], [example]]
(Stream<String> => Stream<Stream<String>>)
Можно собрать в Map по группам (группа -> список слов), а затем развернуть значения. Пример:
Stream<String> src = Stream.of("word", "hello", "dude", "word and word2", "stackoverflow", "question", "ask", "word", "example");
final AtomicInteger group = new AtomicInteger(0);
Stream<Stream<String>> streamResult = src.map(s -> new AbstractMap.SimpleEntry<>(s, s.startsWith("word") ? 0 * group.incrementAndGet() - 1 : group.get()))
.filter(entry -> entry.getValue() > -1)
.collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())))
.values().stream().map(List::stream);
Если нужен результат в виде List<List<String>>
то:
List<List<String>> listResult = new ArrayList<>(src.map(s -> new AbstractMap.SimpleEntry<>(s, s.startsWith("word") ? 0 * group.incrementAndGet() - 1 : group.get()))
.filter(entry -> entry.getValue() > -1)
.collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())))
.values());
Хотя в вашей ситуации непонятно зачем использовать именно stream-ы - проще просто проитерироваться по коллекции и собрать результат в нужном виде.
Раз уж отметился в комментах, то приведу свою реализацию через Collector.
Кстати, данная реализация будет корректно работать и в случае распараллеливания Stream.
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
public final class StringSequenceSplitter
{
private StringSequenceSplitter() { }
private static class Accum
{
private final List<String> buffered = new ArrayList<>();
private final List<List<String>> data = new ArrayList<>();
private final Function<String,Boolean> split_detector;
private final boolean exclude_splitter;
public Accum(Function<String,Boolean> split_detector, boolean exclude_splitter)
{
this.split_detector = split_detector;
this.exclude_splitter = exclude_splitter;
}
public Accum pushElement(String element)
{
boolean new_seq = split_detector.apply(element);
if ( new_seq )
{
List<String> new_list = new ArrayList<>();
if ( ! exclude_splitter )
new_list.add(element);
data.add(new_list);
}
else if ( data.size() > 0 )
data.get(data.size() - 1).add(element);
else
buffered.add(element);
return this;
}
public static Accum combine(Accum a1, Accum a2)
{
if ( a1.data.size() > 0 )
{
a1.data.get(a1.data.size() - 1).addAll(a2.buffered);
a1.data.addAll(a2.data);
}
else
{
a1.buffered.addAll(a2.buffered);
a1.data.addAll(a2.data);
}
return a1;
}
public List<List<String>> getResults()
{
if ( buffered.size() > 0 )
data.add(0, buffered);
return data;
}
}
public static Collector<String, ?, List<List<String>>> of(String splitter, boolean excludeSplitter, boolean ignoreCase)
{
Function<String, Boolean> split_detector =
ignoreCase ?
s -> (s.length() >= splitter.length()) && s.substring(0, splitter.length()).equalsIgnoreCase(splitter) :
s -> s.startsWith(splitter);
return Collector.of
(
() -> new Accum(split_detector, excludeSplitter),
Accum::pushElement,
Accum::combine,
Accum::getResults
);
}
}
Данная реализация Collector возвращает List<List<String>>
, но переделать под Stream<Stream<String>>
- дело несложное.
Пример использования:
Stream.of("word", "hello", "dude", "word and word2", "stackoverflow", "question", "ask", "word", "example")
.parallel() // для демонстрации работы с параллельными Stream
.collect(StringSequenceSplitter.of("word", true, false))
.forEach(list -> System.out.println(" " + list.toString()));
Напоследок, хотелось бы предостеречь автора от неосторожного использования вложенных объектов Stream.
Stream
- объект одноразовый, в том смысле что получить данные из него можно только однажды. Если потребуется повторно запросить данные, то нужно создавать новый объект Stream
(а для этого надо иметь в наличии исходный массив или коллекцию, от которой получали Stream
).
Для иллюстрации "одноразовости" объектов Stream
приведу следующий код.
(даёт ошибку в 3-й строке)
Stream.of(Stream.of("1", "2"), Stream.of("1", "2", "3"))
.filter(stream -> stream.count() > 2)
.forEach(stream -> System.out.println(stream.findFirst().orElse("-")));
P.S.:
Приведённую реализацию Collector можно обобщить на любой тип данных.
Обобщённую реализацию и примеры использования можно глянуть здесь.
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
В чем разница в поведении в случаях если я устанавливаю hibernateconnection
Например: в первом списке мы выбираем страну, появляется новый (ниже первого), там выбираем город, появляется новый и тд пока не дойдем до конечного...
Суть: нужна кнопка которая появляется при определённом событии в 2D игре(не меню)Не всплывающее окно и не JButton
Есть такой вопросУ меня в базе есть пакет с функцией, который возвращает CLOB