Java8 Streams - 比较两个List的对象值并将值添加到第一个列表的子对象?

时间:2018-02-17 01:02:49

标签: arraylist lambda java-8 java-stream

我有两个对象:

public class ObjectOne {

 private String id;
 private String name;
 private String school;
 private String score; //Default score is null

 ..getters and setters..
}

public class ObjectTwo {

 private String id;
 private String marks; 

 ..getters and setters..
}

而且,我有两个上述对象的列表,

List<ObjectOne> listOne;
List<ObjectTwo> listTwo;

如果ID相等,我如何根据条件比较两个列表并将listTwo中的标记分配给listOne的得分。我知道,我们可以使用两个for循环来完成它。但我想用Java8流来实现它。

List<ObjectOne> result = new ArrayList<>();

for(ObjectOne one : listOne) {
    for(ObjectTwo two : listTwo) {
        if(one.getId().equals(two.getId())) {
            one.setScore(two.getmarks());
            result.add(one);
        }
    }
}
return result;

如何使用Java8 lambda和streams实现这一点?

3 个答案:

答案 0 :(得分:5)

listOne.size()为N,listTwo.size()为M. 然后2-for-loops解决方案具有O(M * N)的复杂性。

我们可以通过ids索引listTwo将其减少到O(M + N)。

案例1 - 假设listTwo没有具有相同id的对象

// pair each id with its marks
Map<String, String> marksIndex = listTwo.stream().collect(Collectors.toMap(ObjectTwo::getId, ObjectTwo::getMarks));
// go through list of `ObjectOne`s and lookup marks in the index
listOne.forEach(o1 -> o1.setScore(marksIndex.get(o1.getId())));

案例2 - 假设listTwo具有相同id的对象

    final Map<String, List<ObjectTwo>> marksIndex = listTwo.stream()
            .collect(Collectors.groupingBy(ObjectTwo::getId, Collectors.toList()));

    final List<ObjectOne> result = listOne.stream()
            .flatMap(o1 -> marksIndex.get(o1.getId()).stream().map(o2 -> {
                // make a copy of ObjectOne instance to avoid overwriting scores
                ObjectOne copy = copy(o1);
                copy.setScore(o2.getMarks());
                return copy;
            }))
            .collect(Collectors.toList());

要实现copy方法,您需要创建一个新对象并逐个复制字段,但在这种情况下,我更愿意遵循Builder pattern。它还会产生更多“功能”代码。

答案 1 :(得分:0)

以下代码将ObjectTwo中的标记复制到ObjectOne中得分,如果两个ID相等,则它没有中间对象List<ObjectOne> result

listOne.stream()
    .forEach(one -> {listTwo.stream()
        .filter(two -> {return two.getId().equals(one.getId());})
        .limit(1)
        .forEach(two -> {one.setScore(two.getMarks());});
    });

答案 2 :(得分:0)

这应该有效。

Map<String, String> collect = listTwo.stream().collect(Collectors.toMap(ObjectTwo::getId, ObjectTwo::getMarks));
listOne
   .stream()
   .filter(item -> collect.containsKey(item.getId()))
   .forEach(item -> item.setScore(collect.get(item.getId())));