Java 8 применяет функцию ко всем элементам Stream без нарушения последовательности потоков

Есть ли способ в Java применить функцию ко всем элементам Stream, не разрывая цепочку Stream? Я знаю, что могу вызвать forEach, но этот метод возвращает void, а не 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 для создания и возврата новой жертвы; это позволяет сделать жертву неизменной.