【问题标题】:Java8 Streams - Compare Two List's object values and add value to sub object of first list?Java 8 Streams - 比较两个列表对象值并将值添加到第一个列表的子对象?
【发布时间】:2018-02-17 01:02:48
【问题描述】:

我有两个班级:

public class ClassOne {

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

 ..getters and setters..
}

public class ClassTwo {

 private String id;
 private String marks; 

 ..getters and setters..
}

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

List<ClassOne> listOne;
List<ClassTwo> listTwo;

如果 ID 相等,我如何比较两个列表并将 listTwo 中的标记分配给 listOne 的分数。我知道,我们可以使用两个 for 循环来做到这一点。但我想使用 Java8 流来实现它。

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

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

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

【问题讨论】:

  • 命令式方法,即您当前的方法是可行的方法,除非您需要使用 Java-8 流实现它以从并行性中受益?
  • 即便如此,您仍需要 1) 测量,2) 测量,3) 测量,然后确保将此代码转换为流版本将为您的应用程序带来好处。否则,如果您只是想使用流来使用它,那么请至少表明您这样做的尝试。

标签: arraylist lambda java-8 java-stream


【解决方案1】:

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。它还会产生更多“功能性”代码。

【讨论】:

  • 您假设listTwo 中没有ObjectTwos 共享相同的ID,这可能是错误的。
  • 确实如此,让我看看我是否可以将解决方案推广到重复的可能性
  • 如果我在 o1 本身中设置属性而不创建对象的副本怎么办?
  • @HarishGupta 那么你会在你的结果中得到重复的记录。并且结果不会包含listTwo 中存在的所有标记。
  • @Devstr 我试过了,结果没有给我重复的记录。
【解决方案2】:

以下代码从 ObjectTwo 复制标记到 ObjectOne 中,如果两个 id 相等,则没有中间对象List&lt;ObjectOne&gt; result

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

【讨论】:

    【解决方案3】:

    这应该可行。

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

    【讨论】:

      猜你喜欢
      • 2019-12-06
      • 2019-11-16
      • 2019-01-04
      • 2019-09-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-17
      • 2020-04-10
      • 1970-01-01
      相关资源
      最近更新 更多