I have a requirement related to Streams in Java. I need to iterate over a List of Objects,where each object has Integer property and a List property.
What I need is, if same objects has the same ID, I need to concat the lists. Let me illustrate the example with a little bit of a simple code:
Here just defining 2 simple classes:
public static class Wrapper {
Integer id ;
List<Element> list;
public Wrapper(Integer id, List<Element> list) {
this.id = id;
this.list = list;
}
}
public static class Element {
String content ;
public Element(String content) {
this.content = content;
}
}
Now in a Main Java method,creating same objects for the porpuse of the example:
List<Wrapper> list=new ArrayList();
ArrayList<Element> listForWrapper1= new ArrayList();
listForWrapper1.add(new Element("Content A"));
listForWrapper1.add(new Element("Content B"));
ArrayList<Element> listForWrapper2= new ArrayList();
listForWrapper2.add(new Element("Content C"));
listForWrapper2.add(new Element("Content D"));
ArrayList<Element> listForWrapper3= new ArrayList();
listForWrapper3.add(new Element("Content E"));
listForWrapper3.add(new Element("Content F"));
Wrapper wrapper1=new Wrapper(1,listForWrapper1);
Wrapper wrapper2=new Wrapper(2,listForWrapper2);
//Here this Wrapper has the same ID than wrapper2
Wrapper wrapper3=new Wrapper(2,listForWrapper3);
//Adding Elements to List
list.add(wrapper1);
list.add(wrapper2);
list.add(wrapper3);
As you can see, I am adding 3 Wrappers to the list, BUT 2 of them have the same ID
What I want is when Wrapper IDs are the same in the array,just merge both list. So in this example the result should be:
A list with 2 Element :
Element 1 : Wrapper Object with ID 1,with 2 Elements inside its list property,Element Content A ,and Element Content B
Element 2 : Wrapper Object with ID 2,with 4 Elements inside its list property,Element Content C,Element Content D,Element Content E and Element Content F.
How can I achieve this result using Streams? I cant think any elegant solution!
Thanks in advance!
List<Wrapper> combinedList=list.stream().....
You could use BinaryOperator<U> mergeFunction
in Collectors.toMap`.
Collection<Wrapper> wrapperList = wrappers.stream()
.collect(Collectors.toMap(Wrapper::getId, x -> x),
(oldVal, newVal) -> {
oldVal.getElements().addAll(newVal.getElements());
return oldVal;
}))
.values();
In the above code I have written mergeFunction
to always return oldVal (oldVal, newVal) -> oldVal
but you can change the way you want. Lambda function x -> x
can also be written as Function.identity()
.
You can use Collectors.toMap()
to add the values of the map using a merge function.
Map<Integer, Wrapper> collect =
list.stream()
.collect(Collectors.toMap(w -> w.id,
w -> w,
(w1, w2) -> {
w1.list.addAll(w2.list);
return w1;
})
);
Working
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Wrapper> list=new ArrayList();
ArrayList<Element> listForWrapper1= new ArrayList();
listForWrapper1.add(new Element("Content A"));
listForWrapper1.add(new Element("Content B"));
ArrayList<Element> listForWrapper2= new ArrayList();
listForWrapper2.add(new Element("Content C"));
listForWrapper2.add(new Element("Content D"));
ArrayList<Element> listForWrapper3= new ArrayList();
listForWrapper3.add(new Element("Content E"));
listForWrapper3.add(new Element("Content F"));
Wrapper wrapper1=new Wrapper(1,listForWrapper1);
Wrapper wrapper2=new Wrapper(2,listForWrapper2);
//Here this Wrapper has the same ID than wrapper2
Wrapper wrapper3=new Wrapper(2,listForWrapper3);
//Adding Elements to List
list.add(wrapper1);
list.add(wrapper2);
list.add(wrapper3);
Map<Integer, Wrapper> collect =
list.stream()
.collect(Collectors.toMap(w -> w.id,
w -> w,
(w1, w2) -> {
w1.list.addAll(w2.list);
return w1;
})
);
System.out.println( collect.values() );
}
}
class Wrapper {
Integer id ;
List<Element> list;
public Wrapper(Integer id, List<Element> list) {
this.id = id;
this.list = list;
}
@Override
public String toString() {
return id + ":" + list;
}
}
class Element {
String content ;
public Element(String content) {
this.content = content;
}
@Override
public String toString() {
return content;
}
}
Output
[1:[Content A, Content B], 2:[Content C, Content D, Content E, Content F]]
You can try this:
Map<Integer, Wrapper> map = list.stream().collect(Collectors.toMap(wrapper -> wrapper.id /*Use the ids as the keys*/, wrapper -> wrapper /*Return the same wrapper as the value*/, (w1, w2) -> {
w1.list.addAll(w2.list); // If a wrapper with the same id is found, then merge the list of wrapper 2 to the list of wrapper 1 and return wrapper 1.
return w1;
}));
list = new ArrayList<>(map.values()); // Create new ArrayList with the values of the map.
System.out.println(list); // [cci.Test$Wrapper@4eec7777, cci.Test$Wrapper@3b07d329]
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.