1

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().....
1
  • How do you determine which wrapper has an already used id? Commented May 15, 2019 at 18:29

3 Answers 3

3

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().

Sign up to request clarification or add additional context in comments.

4 Comments

(oldVal, newVal) -> oldVal) will erase newVal's Elements.
I mentioned it explicitly in my answer and asked him to create new Wrapper the way he wants.
Why not explicitly tell him to do (oldVal, newVal) -> { oldVal.getElements.addAll(newVal.getElements()); return oldVal; } or better with a function addElements.
@ Rajkumar Natarajan Thanks for the answer.I dont know which should be the accepted answer of all of them!
1

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]]

Comments

0

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]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.