Додавання нового значення до існуючого потоку


79

Чи є хороший спосіб додати нову вартість до існуючої 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і мені цікаво, чому. Заздалегідь спасибі.


2
Немає гарантії того, що Listповернуте з collect(Collectors.toList())підтримки add, Collectors.toCollectionзамість цього ви можете вибрати тип отриманого списку.
Алекс - GlassEditor.com

Відповіді:


97

Питання заперечує неправильне припущення: що потоки насправді містять свої дані. Вони не; потоки не є структурами даних, вони є засобом для визначення масових операцій у різних джерелах даних.

Існують комбінатори для об'єднання двох потоків в один, наприклад Stream.concat, і фабрики для створення потоків з набору відомих елементів ( Stream.of) або з колекцій ( Collection.stream). Отже, ви можете комбінувати їх, якщо хочете створити новий потік, який є об’єднанням потоку, який у вас є, разом із новим потоком, що описує нові елементи.

Проблема у вашому прикладі PECS полягає в тому, що у вас є три випадки ? super T, і ви припускаєте, що вони описують один і той же тип, але вони цього не роблять. Кожне входження символу підстановки відповідає унікальному захопленню, яке не є тим, що ви хочете; вам потрібно дати цій змінні типу ім’я, щоб компілятор знав, що тип списку та тип вхідного потоку однакові. (Крім того, не матеріалізуйте колекцію; це дорого і, можливо, не припиняється, якщо потік не є кінцевим. Просто використовуйте concat.) Отже, відповідь така: ви щойно помилились із дженериками. Ось один із способів зробити це:

public<T> Stream<T> appendToStream(Stream<? extends T> stream, T element) {
    return Stream.concat(stream, Stream.of(element));
}

Ви переплутали себе з PECS, тому що думали про те, щоб "вставити" в потік, а насправді ви його споживаєте.


7
Метод не повинен називатися "appendToStream"? В іншому випадку я думаю, що параметри методу concat слід змінити.
Andrea Polci

concatметод може використовувати, але не надто багато, від javadoc: Будьте обережні, будуючи потоки з повторної конкатенації. Доступ до елемента глибоко об’єднаного потоку може призвести до глибоких ланцюжків викликів або навіть до StackOverflowException.
TongChen

34

Як щодо

return Stream.concat(stream, Stream.of(elem));

це припускає, що початковий потік скінченний. Якщо це не так, ви можете зібрати їх у зворотному порядку.


Будьте обережні, будуючи потоки з повторної конкатенації. Доступ до елемента глибоко об’єднаного потоку може призвести до глибоких ланцюжків викликів або навіть до StackOverflowException.
TongChen

5

Бібліотека StreamEx має відповідні методи #prepend()та #append()методи. Ось приклад того, як їх можна використовувати:

StreamEx.of("second").prepend("first").append("third").forEach(System.out::println);

Вихід такий:

first
second
third

Приємно! Хоча, ще одна бібліотека.
GhostCat

0

Найкращий спосіб - використовувати 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());

Скільки разів ви хочете додати елемент? : D Унція на потік або один раз на елемент у вихідному потоці?
користувач482745
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.