如何在针对每个键删除一些条目后从一个映射中收集值,并使用流生成另一个映射(Java 8)

时间:2015-01-16 12:34:45

标签: collections java-8 java-stream

我有一张地图。在映射中对应于每个键,有一个包含50个对象的List。基本上我使用Streams制作了这张地图。我做了什么,我使用了流的groupingBy函数。以下是我采取的步骤。

Map<Long, List<Learner>> learnersMap = learnersDataList.stream()
            .collect(Collectors.groupingBy(Learner::getLearnerEnrollmentId));

然后,我使用下面的代码将每个密钥少于50个或等于50个计数的记录分开

Map<Long, List<Learner>> answersLessThan50CountLearnersMap = 
             learnersMap.entrySet()
            .stream()
            .filter(p -> p.getValue().stream().count() < 50)
            .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));

Map<Long, List<Learner>> validLearnersMap = 
            learnersMap.entrySet()
            .stream()
            .filter(p -> p.getValue().stream().count() == 50)
            .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));

现在我希望从answersLessThan50CountLearnersMap开始,我生成一个只包含List中一条记录的地图。我的意思是假设Map看起来像这样

1-->[Learner, Learner, Learner, ....]
2-->[Learner, Learner, Learner, ....]
3-->[Learner, Learner, Learner, ....]

然后我想制作一个像这样的地图

1--> Learner
2--> Learner
3--> Learner

实际上,列表中的所有记录都是多余的。像所有50个学习者中的名字一样,对象1的对象是相同的。只有一条记录是截然不同的,但我不需要,因为我必须通过从学习者对象获取学习者记录来发送电子邮件,而不需要那些不同的记录。密钥23也是如此。就像我可以说那样

entrySet.getValue.stream.findFirst() or entrySet.getValue.stream.limit(1)

对于entrySet中的每个条目。换句话说,我想从Map<Long, Learner>

生成answersLessThan50CountLearnersMap之类的地图

我可以通过对其应用流操作来实现。请帮忙

由于

2 个答案:

答案 0 :(得分:1)

请注意,并非因为您使用的是Stream s。

,所以一切都更好

跳入我眼中的第一件事是p.getValue().stream().count(),而p.getValue()实际上会返回List。所以p.getValue().size()在这里显然更合理。

此外,如果您只想要List的第一个元素,则调用list.get(0)是一种行之有效的方法,比list.stream().findFirst()更简单。如果列表的大小可能为零,则必须检查,但在您的一个案例中,它已被证明是非零的(因为size等于50强制要求{ {1}})。

您可以在定义Predicate应包含的值的位置执行此操作:

Map

如果你想放弃效率的紧凑性,你可能会认为你只对每个元素的第一个元素感兴趣,就在你创建第一个地图时,换句话说,你需要的只是第一个元素和计数而不是比完全填满Map<Long, Learner> answersLessThan50CountLearnersMap = learnersMap.entrySet().stream() .filter(p -> p.getValue().size() < 50) .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue().isEmpty()? null: p.getValue().get(0))); Map<Long, Learner> validLearnersMap = learnersMap.entrySet().stream() .filter(p -> p.getValue().size() == 50)// implies non-empty list .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue().get(0))); 。所以我们需要一个替代容器,只容纳第一个元素和计数,适合收集数据:

List

收集信息

class LearnerAndCount {
    Learner first;
    int count;
    LearnerAndCount add(Learner l) {
        if(first==null) first=l;
        count++;
        return this;
    }
    LearnerAndCount merge(LearnerAndCount lac) {
        if(first==null) first=lac.first;
        count+=lac.count;
        return this;
    }
}

并用它来创建两个地图

Map<Long, LearnerAndCount> learnersMap = learnersDataList.stream().collect(
  Collectors.groupingBy(Learner::getLearnerEnrollmentId,
    Collector.of(LearnerAndCount::new, LearnerAndCount::add, LearnerAndCount::merge)));

答案 1 :(得分:0)

但我不知道因为我是溪流的新手有多好。这就是我做的。

Map<Long, Learner> invalidLearnersMap = 
            answersLessThan50CountLearnersMap.entrySet()
            .stream()
            .collect(Collectors.toMap(p -> p.getKey(), p ->  {
                Optional<Learner> learner = p.getValue().stream().findFirst();
                if (learner.isPresent()) {
                    Learner learner = learner.get();
                    return learner;
                } else {
                    return null;
                }

            }));

我也可以这样做lessThan50CountMap。无论如何谢谢。