Аналог Stream.reduce java
Привет) Я никак не могу найти аналог методу reduce в java stream api, только у этого аналога по сравнению с основным методом должно быть одно отличие - мне нужно избежать операции сведения всего потока в какой-то один объект. Возможно, можно было бы использовать Collectors(поскольку на выходе мне нужен список), но я не представляю, как это можно провернуть (параметр identity все ломает)
Пример того, чего бы мне очень хотелось добиться:
Предположим, у меня есть список строк, из которых мне нужно в одной операции потока замапить их в объект, при этом, если следующий элемент удовлетворяет какому-то условию, прикрутить его к первому. О чем я:
class SomeProperty {
String someField;
Srring property;
//allArgsConstructor
}
//THEN
List<SomeProperty> list = List.of(...elements...).stream()
.reduce((x,y)->{
if(y.equals("i am property!") {
return new SomeProperty(x, y) ;
} else {
return new SomeProperty(x, "default") ;
}
}).collect(Collectors.toList());
Как то в этом духе) У кого есть идеи, поделитесь, пожалуйста!
Ответы (2 шт):
Вот работающий вариант. Выглядит страшно, но функциональная парадигма вся об этом.
package yourpackage;
import java.util.ArrayList;
import java.util.List;
import static yourpackage.Main.Data.FIELD;
import static yourpackage.Main.Data.PROPERTY;
public class Main {
enum Data {
FIELD, PROPERTY
}
public static void main(String[] args) {
List<List<Data>> result =
List.of(FIELD, PROPERTY, FIELD, FIELD, PROPERTY, PROPERTY, PROPERTY)
.stream()
.map(d -> List.of(List.of(d)))
.reduce(new ArrayList<List<Data>>(),
(fieldList, d) -> {
if (d.get(0).get(0) == FIELD) {
var list = new ArrayList<Data>();
list.add(d.get(0).get(0));
fieldList.add(list);
} else {
fieldList
.get(fieldList.size() - 1)
.add(d.get(0).get(0));
}
return fieldList;
});
System.out.println(result);
}
}
Вывод следующий
[[FIELD, PROPERTY], [FIELD], [FIELD, PROPERTY, PROPERTY, PROPERTY]]
Здесь и далее "массив массивов" (тип ответа) буду обозначать как "супермассив"
В кратце: мы хотим получить супермассив, где в каждом массиве первый элемент FIELD а следующие PROPERTY. Для этого мы оборачивем каждое значение в два массива, лишь затем, чтобы их тип совпал с супермассивом (строгая типизация в Java, мда). При reduce, если вновь пребывший элемент - [[FIELD]], то мы создаем новый массив вида [FIELD] и добавляем в конец супермассива, а если [[PROPERTY]], то мы берем последний массив из супермассива, который представляет последний FIELD и его свойства, и добавляем еще одно.
Так же замечу, что код падает, если первый элемент есть FIELD.
Этот пример легко сводится к вашей задаче, достаточно сделать один map.
Развитие предидущего ответа:
enum Data {
FIELD, PROPERTY
}
static class SomeProperty {
private String someField;
private List<String> property = new ArrayList<>();
public SomeProperty(String someField, List<String> property) {
this.someField = someField;
this.property = property;
}
public SomeProperty(String someField) {
this.someField = someField;
}
@Override
public String toString() {
return "SomeProperty{" +
"someField='" + someField + '\'' +
", property=" + String.join(", ", property) +
'}';
}
}
public static void main(String[] args) {
List<SomeProperty> result = List.of(FIELD, PROPERTY, FIELD, FIELD, PROPERTY, PROPERTY, PROPERTY)
.stream()
.collect(Collector.of(
LinkedList::new,
(acc, v) -> {
if (v == FIELD) {
acc.offer(new SomeProperty(v.name()));
} else {
Optional.ofNullable(acc.peek()).ifPresent(p -> p.property.add(v.name()));
}
},
(acc1, acc2) -> {
acc1.addAll(acc2);
return acc1;
}));
System.out.println(result);
}
reduce не очень-то подходит для подобных случаев, поскольку его аккумулятор должен быть того же типа, что и текущий элемент. Потому я предлогаю пользоваться для таких случаев Collector.of
Логика такая: Если текущий элемент - Field, создаем новый SomeProperty с properties по умолчанию (в этом примере пустая коллекция) и загоняем в очередь. В ином случае, берем верх очереди (если он есть) и добавляем ему property
Блок:
(acc1, acc2) -> {
acc1.addAll(acc2);
return acc1;
}
Нужен только для случая, когда используется parallelStream для комбинирования параллельных результатов и в данном случае может быть проигнорирован.