首页 > 解决方案 > 如何使用 Stream 改进此 List 迭代?

问题描述

我正在尝试计算在对象列表中的字段中看到 Int 的次数。

这是我的代码

TreeMap<Integer, Double> ratings = new TreeMap();
ArrayList<Establishment> establishments = new ArrayList<>();

double one = 0;
double two = 0;
double three = 0;
double five = 0;

for (Establishment e : establishments) {
    if (e.getRating() == 1) {
        one++;
    }
    if (e.getRating() == 2) {
        two++;
    }
    if (e.getRating() == 3) {
        three++;
    }
    if (e.getRating() == 5) {
        five++;
    }
}

    ratings.put(1, (one / establishments.size()) * 100);
    ratings.put(2, (two / establishments.size()) * 100);
    ratings.put(3, (three / establishments.size()) * 100);
    ratings.put(5, (five / establishments.size()) * 100);

但这并不理想,如果添加更多评分(例如 20+),那么您将创建一堆双打,并且不可维护。

我知道如果我有一个整数列表,我可以用 Stream 做一些事情,比如说

listOfInts.stream().filter(i -> i == 3).count()

然而,这是一个对象列表,其中包含一个 int,我需要计算该对象列表中的评级数 == X。

所以我需要的伪代码:

建立emnts.getAllRatings().stream().filter(ratings -> rating == 3).count()*

* 对每个评级类型重复 1 - 5

** 没有 getAllRatings - 我想这就是我要解决的问题)

标签: javalambdajava-8java-stream

解决方案


你可以做:

Map<Integer, Double> ratings = new TreeMap<>();
List<Establishment> establishments = new ArrayList<>();
establishments.stream() 
              .collect(Collectors.groupingBy(Establishment::getRating, Collectors.counting()))
              .forEach((k, v) -> ratings.put(k, (double)v/establishments.size() * 100));

哪个将与 一起使用Collectors::groupingByCollectors::counting这将创建一个Map由评分计数组成的,然后用于forEach将它们添加到TreeMap

或者,正如 VGR 所建议的,更优雅地使用Collectors::collectingAndThen:

Map<Integer, Double> ratings =
establishments.stream()
              .collect(
                       Collectors.groupingBy(Establishment::getRating, 
                       Collectors.collectingAndThen(Collectors.counting(), c -> c * 100.0 / establishments.size())
               ));

这将直接创建,Map而无需创建 a Map,再次流过它,然后再次收集到Mapa


推荐阅读