如何在比较后使用java流API合并两个列表



我有两个相同类型的列表:Boxes1和Boxes2:

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

"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
}
]
}
]
}
]
我想检查是否有boxIdboxes1匹配boxIdboxes2,然后我要添加字符eleIdbox1box2组合后的结果如下所示:
"box2": [{
"boxId": "ABC",
"ele": [{
"eleId": "8040",
"chars": [{
"no": "123",
"qty": 6
},
{
"no": "123",
"qty": 2
}
]
},.....................

如果有boxIdBoxes1Boxes2我想把这个方框添加到Boxes2中. 最后应该是这样的:

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

我是新的流,所以尝试使用旧的方式,但它有问题:

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);
}
});
});

使用Streams对您的情况没有多大帮助。我建议使用POIC(普通的老式命令式代码)。

伪码算法:
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);
}
}

对于Streams,我唯一要考虑的是"找到匹配的ele并添加字符";部分。但是对于庞大的集合,这会变得很慢。

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)

你当然也可以对Streams进行box1迭代。但这将导致一些非常丑陋和不可读的代码。(编辑:就像你的例子代码:不太好。)

你可以做的是:将相关数据放在Map中,以便更快更容易地访问,而不需要所有这些循环。

另一个解决方案是这样的:

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

mergeEle方法:

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());
}

andmergeChar:

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

最新更新