简体   繁体   中英

How to merge two lists after comparing using java streams API

I have two lists of same type: Boxes1 and Boxes2:

"boxes1": [{
        "boxId": "ABC",
        "ele": [{
                "eleId": "8040",
                "chars": [{
                        "no": "123",
                        "qty": 2
                    }
                ]
            }
        ]
    }, {
        "boxId": "XYZ",
        "ele": [{
                "eleId": "1212",
                "chars": [{
                        "no": "456",
                        "qty": 3
                    }
                ]
            }
        ]
    }
]

And

"boxes2": [{
        "boxId": "ABC",
        "ele": [{
                "eleId": "8040",
                "chars": [{
                        "no": "123",
                        "qty": 6
                    }
                ]
            }, {
                "eleId": "4560",
                "chars": [{
                        "no": "012",
                        "qty": 3
                    }
                ]
            }
        ]
    }, {
        "boxId": "PQR",
        "ele": [{
                "eleId": "1111",
                "chars": [{
                        "no": "456",
                        "qty": 8
                    }
                ]
            }
        ]
    }
]

I want check if any boxId from boxes1 matches with a boxId from boxes2 , then I want to add the chars of the a common element eleId of box1 into box2 so that the result looks like this after combining:

"box2": [{
        "boxId": "ABC",
        "ele": [{
                "eleId": "8040",
                "chars": [{
                        "no": "123",
                        "qty": 6
                    },
                    {
                        "no": "123",
                        "qty": 2
                    }
                ]
            },.....................

and also if there is any boxId from Boxes1 that is not present in Boxes2 I want to add that box inside the Boxes2 . so finally it should look like:

"box2": [{
        "boxId": "ABC",
        "ele": [{
                "eleId": "8040",
                "chars": [{
                        "no": "123",
                        "qty": 6
                    }, {
                        "no": "123",
                        "qty": 2
                    }
                ]
            }, {
                "eleId": "4560",
                "chars": [{
                        "no": "012",
                        "qty": 3
                    }
                ]
            }
        ]
    }, {
        "boxId": "PQR",
        "ele": [{
                "eleId": "1111",
                "chars": [{
                        "no": "456",
                        "qty": 8
                    }
                ]
            }
        ]
    }, {
        "boxId": "XYZ",
        "ele": [{
                "eleId": "1212",
                "chars": [{
                        "no": "456",
                        "qty": 3
                    }
                ]
            }
        ]
    }
]

I am new to Stream so tried using the old way but it has issue:

boxes2.forEach(box2 -> {
            boxes1.forEach(box1 -> {
                if (box1.getBoxId().equals(box2.getBoxId())) {
                    box2.getEle().forEach(ele1 -> {
                        box1.getEle().forEach(ele2 -> {
                            if (ele1.getEleId().equals(ele2.getEleId())) {
                                ele1.chars().addAll(ele2.chars());
                            }
                        });
                    });
                } else {
                    boxes2.add(box1);
                }
            });
        });

Using Stream s won't help much in your case. I'd suggest to go for POIC (plain old imperative code).

Pseudo code alorithm:

for ( box1 of boxes1 ) {
  var box1Id = box.boxId;
  if ( boxes2.containsBoxId(box1Id) ) {
    var box2 = boxes2.getBoxById(boxId);
    
    for ( ele1 of box1.ele ) {
      var ele1Id = ele1.eleId;
      if ( box2.containsEleId(ele1Id) ) {
        var ele2 = box2.getEleById(ele1Id);

        ele2.addToChars(ele1.chars);
      }
    }
  
  } else {
    // if boxes2 doesn't contain box1Id
    boxes2.addBox(box1);
  }
}

The only thing I would consider for Stream s is the "find matching ele and add chars" part. But that will get slow for huge collections.

var box1Id = ...
var box1ele = ...
boxes2.stream()
  .filter(b -> b.boxId.equals(box1Id))
  .flatMap(b -> b.ele.stream())
  .filter(e -> e.eleId.equals(box1ele))
  .forEach(e -> e.addChars(box1ele.chars)

You could of course do the box1 iteration with Stream s, too. But that will lead to some really ugly and unreadable code. (EDIT: Just like your example code: Not nice.)

What you could do instead: Put the relevant data in a Map for faster and easier access without all those loops.

An other solution is like this:

List<Box> boxResult = Stream.concat(boxes1.stream(), boxes2.stream())
            .collect(toMap(Box::getBoxId, 
                           v-> new ArrayList<>(v.getEle()),
                           YOUR_CLASS::mergeEle))
            .entrySet().stream()
            .map(entry -> new Box(entry.getKey(), entry.getValue()))
            .collect(Collectors.toList());

and mergeEle method:

private static List<Ele> mergeEle(List<Ele> l1, List<Ele> l2) {
    return Stream.concat(l1.stream(), l2.stream())
            .collect(toMap(Ele::getEleId,
                           v -> new ArrayList<>(v.getChars()),
                           YOUR_CLASS::mergeChar))
            .entrySet().stream()
            .map(e -> new Ele(e.getKey(), e.getValue()))
            .collect(Collectors.toList());
}

and mergeChar :

private  List<Char> mergeChar(List<Char> c1, List<Char> c2) {
    c1.addAll(c2);
    return c1;
}

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM