Добавление нового значения в существующий поток
Есть ли хороший способ добавить новое значение в существующий Stream
? Все, что я могу себе представить, это примерно так:
public <T> Stream<T> addToStream(Stream<T> stream, T elem ) {
List<T> result = stream.collect(Collectors.toList());
result.add(elem);
return result.stream();
}
Но я ищу что-то более сжатое, что я могу использовать в выражении лямбда без многословия.
Другой вопрос появился, когда я попытался реализовать принцип PECS:
public <T> Stream<? super T> addToStream(Stream<? super T> stream, T elem ) {
List<? super T> result = stream.collect(Collectors.toList()); //error
result.add(elem);
return result.stream();
}
Кажется, что подстановочный символ не работает с Stream.collect
, и мне интересно, почему. Спасибо заранее.
Ответы
Ответ 1
Вопрос противоречит неверному предположению: потоки фактически содержат свои данные. Они не; потоки не являются структурами данных, они являются средством для задания массовых операций в различных источниках данных.
Комбинаторы объединяют два потока в один, например Stream.concat
, и фабрики для создания потоков из набора известных элементов (Stream.of
) или из коллекций (Collection.stream
). Таким образом, вы можете комбинировать их, если хотите создать новый поток, который является конкатенацией потока, который у вас есть, наряду с новым потоком, описывающим новые элементы.
Проблема в вашем примере PECS состоит в том, что у вас есть три вхождения ? super T
, и вы предполагаете, что они описывают один и тот же тип, но они этого не делают. Каждое появление подстановочного символа соответствует уникальному захвату, который не то, что вы хотите; вам нужно указать переменную типа name, чтобы компилятор знал, что тип списка и тип входного потока совпадают. (Кроме того, не материализуйтесь в коллекции, это дорогостоящее и потенциально не заканчивающееся, если поток не является конечным. Просто используйте concat.) Итак, ответ таков: вы просто получили дженерики неправильно. Вот один из способов сделать это:
public<T> Stream<T> appendToStream(Stream<? extends T> stream, T element) {
return Stream.concat(stream, Stream.of(element));
}
Вы путали себя с PECS, потому что вы думали о "вставке" в поток, когда на самом деле вы его потребляете.
Ответ 2
Как насчет
return Stream.concat(stream, Stream.of(elem));
это предполагает, что исходный поток конечен. Если это не так, вы можете выполнить их в обратном порядке.
Ответ 3
Библиотека StreamEx имеет соответствующие #prepend()
и #append()
. Вот пример, как их можно использовать:
StreamEx.of("second").prepend("first").append("third").forEach(System.out::println);
Вывод выглядит следующим образом:
first
second
third
Ответ 4
- Преобразовать поток в список
- Добавить новый предмет
- Конвертировать обратно в поток
Ответ 5
Лучший способ - использовать flatMap следующим образом:
public <T> Stream<T> appendToStream(Stream<T> stream, T element) {
return stream.flatMap(e -> Stream.of(e, element));
}
Это работает с исходным потоком, поэтому это может быть просто еще одна промежуточная операция в потоке, например:
stream.flatMap(e -> Stream.of(e, element))
.map(...)
.filter(...)
.collect(Collectors.toList());