Есть ли способ в Java применить функцию ко всем элементам Stream
, не разрывая цепочку Stream
? Я знаю, что могу вызвать forEach
, но этот метод возвращает void
, а не Stream
.
Java 8 применяет функцию ко всем элементам Stream без нарушения последовательности потоков
Ответ 1
Есть (по крайней мере) 3 способа. Для примера кода я предположил, что вы хотите вызвать 2 метода пользователя methodA
и methodB
:
а. Используйте peek()
:
list.stream().peek(x -> methodA(x)).forEach(x -> methodB(x));
Хотя документы говорят, что используют его только для "отладки", он работает (и сейчас он работает)
В. Используйте map()
для вызова метода A, затем верните элемент обратно в поток:
list.stream().map(x -> {method1(x); return x;}).forEach(x -> methodB(x));
Это, вероятно, самый "приемлемый" подход.
С. Сделайте две вещи в forEach()
:
list.stream().forEach(x -> {method1(x); methodB(x);});
Это наименее гибкий и может не соответствовать вашим потребностям.
Ответ 2
Вы ищете функцию Stream
map()
.
Пример:
List<String> strings = stream
.map(Object::toString)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
Ответ 3
Не совсем уверен, что вы подразумеваете под breaking the stream chain
, но любая операция в Stream
, которая возвращает Stream
, не будет ломать или потреблять ваш поток. Потоки потребляются terminal operations
, и, как вы заметили, forEach
не возвращает Stream<T>
и, как таковой, завершает поток, выполняя все операции intermediate
перед самим forEach и самим forEach.
В примере, который вы указали в комментариях:
myStream.map(obj -> {obj.foo(); return obj;}
Вы не можете сделать это с помощью одного лайнера. Конечно, вы могли бы использовать ссылку на метод, но тогда возвращаемый Stream
был бы другого типа (если foo
возвращает тип):
myStream.map(Obj::foo) // this will turn into Stream<T>, where T is
// the return type of foo, instead of Stream<Obj>
Кроме того, ваша операция map
stateful
, что сильно не рекомендуется. Ваш код будет скомпилирован и даже может работать так, как вы хотите, но позже он может выйти из строя. Операции map
должны быть stateless
.
Ответ 4
Лучший вариант, который у вас есть, - применить карту к вашему потоку. который возвращает поток, состоящий из результатов применения данной функции к элементам этого потока, например:
IntStream.rangeClosed(40, 70).limit(20).mapToObj(i -> (Integer) i).map(item->item+3).map(item->item*2)...
(поток элементов типа StreamItemABC)
Мы применяем несколько изменений к потоку, но таким образом мы не можем передать какие-либо аргументы методу карты, чтобы исправить его:
private void applyMethod(ParameterX parX) {
someStreamX().map(n -> methodX(n, parX))...
}
private StreamItemABC methodX (StreamItemABC item, ParameterX parX) {
return notification;
}
Ответ 5
Я думаю, что вы ищете Stream.peek
. Но внимательно прочитайте документы, поскольку он был разработан главным образом как метод отладки. Из документов:
Этот метод существует в основном для поддержки отладки, когда вы хотите видеть элементы при прохождении мимо определенной точки в конвейере
Действие, переданное в peek
, должно быть не мешает.
Ответ 6
Я думаю, что самый чистый способ - добавить мутатор к объектам в потоке.
Например,
class Victim {
private String tag;
private Victim withTag(String t)
this.tag = t;
return this;
}
}
List<Victim> base = List.of(new Victim());
Stream<Victim> transformed = base.stream().map(v -> v.withTag("myTag"));
Если вы предпочитаете (и многие захотят), вы можете использовать метод withTag для создания и возврата новой жертвы; это позволяет сделать жертву неизменной.