Подтвердить что ты не робот

Добавление нового значения в существующий поток

Есть ли хороший способ добавить новое значение в существующий 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, и мне интересно, почему. Спасибо заранее.

4b9b3361

Ответ 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

  1. Преобразовать поток в список
  2. Добавить новый предмет
  3. Конвертировать обратно в поток

Ответ 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());