Как правильно работать с методом reduce в Stream API?
Всем привет, подскажите, как переписать использования метода reduce. Результат, которого хочется добиться: Задать в методе reduce значение по умолчанию accumulator, например new ArrayList<>() и чтобы, на каждой итерации в этот ArrayList я добавлял currentValue.
package com.example.demo;
import java.util.ArrayList;
import java.util.List;
class DataObj {
private String id;
private String title;
public DataObj(String id, String title) {
this.id = id;
this.title = title;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "DataObj{" +
"id='" + id + '\'' +
", title='" + title + '\'' +
'}';
}
}
public class Test {
public static void main(String[] args) {
List<DataObj> dataObjs = new ArrayList<>();
DataObj dataObj1 = new DataObj("1", "one");
DataObj dataObj2 = new DataObj("2", "two");
DataObj dataObj3 = new DataObj("3", "three");
DataObj dataObj10 = new DataObj("10", "ten");
dataObjs.add(dataObj1);
dataObjs.add(dataObj2);
dataObjs.add(dataObj3);
dataObjs.add(dataObj10);
System.out.println("dataObjs " + dataObjs);
// List<DataObj> output = new ArrayList<>();
DataObj result = dataObjs.stream().reduce(dataObjs.get(0), (accumulator, currentValue) -> {
// System.out.println("accumulator " + accumulator);
return accumulator;
});
System.out.println("result " + result);
}
}
Ответы (1 шт):
Можно попробовать так.
ArrayList<DataObj> r = dataObjs.stream().reduce(
new ArrayList<>(),
(acc, curr) -> { acc.add(curr); return acc; },
(acc1, acc2) -> { acc1.addAll(acc2); return acc1; }
);
Но это если уверены, что обработка стрима не распараллеливается.
Потому, что редукция каждого куска должна начинаться с одного и того же начального значения identity (new ArrayList<>()).
Но в таком аккумуляторе он сразу же изменяется. И вообще каждый поток будет вести запись в один список в разнобой. А потом ещё окажется, что этот список будет объединяться сам с собой. Что же получится в результате?
Правильно сделать наверное как-то так.
ArrayList<DataObj> r = dataObjs.stream().reduce(
new ArrayList<>(),
(acc, curr) -> {
if (acc.isEmpty()) {
acc = new ArrayList<>();
}
acc.add(curr);
return acc;
},
(acc1, acc2) -> {
if (acc1.isEmpty()) return acc2;
acc1.addAll(acc2);
return acc1;
}
);
Т.е. в аккумуляторе проверяется, что acc является identity. identity пустой, acc планируется заполнять. Поэтому identity легко опознать по isEmpty. И вместо identity для дальнейшей обработки подсунуть новый список.
Вторая функция, если кто не знает, используется для сбора "параллельных" кусков в одно целое. Тоже модифицирована, чтобы случайно не испортить identity.
И вообще у стрима для сбора коллекция есть спец.методы collect.