使用 Lambda 在 Java 8 中计算频率(专业)

问题描述 投票:0回答:3

我有一个这样的字符串列表:“na”,“na”,“na”,“uk”,“uk”。我想以这样的方式计算每个元素的频率:如果现有映射的值是偶数,那么我将添加 1;否则 2.

 List<String> streamer = Arrays.asList("na", "na", "na", "uk", "uk");
 Map<String, Integer> m = new HashMap<>();

 for(String s:streamer) {
     if (m.containsKey(s)) {
         if(m.get(s) % 2 == 0)
             m.put(s, m.get(s) + 1);
         else
             m.put(s,m.get(s)+2);
     }
     else
         m.put(s,1);
  }
  System.out.println("CUSTOM Frequency::::" + m); 

现在,我想使用流和 lambda 实现完全相同的事情。

我能做的就是这样:

Map<String, Long>map4 = streamer.stream()
    .collect(Collectors.groupingBy(
        Function.identity(),
        Collectors.counting())); // How can I get custom counting instead of Collectors.counting() ?
java lambda hashmap java-stream
3个回答
3
投票

我想以这样的方式计算每个元素的频率: 现有地图的值为even,那么我将添加

1
;否则
2

要按照您所描述的方式执行计数,我们可以这样做,而不是创建一个 自定义收集器 或使用组合 groupingBy +

collectionAndThen
+
counting
将三个标准
收集器
相互嵌套通过使用 single 内置 collector
toMap()

代码简洁且具有描述性:

public static Map<String, Integer> oddEvenCount(Collection<String> strings) {
    
    return strings.stream()
        .collect(Collectors.toMap(
            Function.identity(),                      // key mapper
            str -> 1,                                 // value mapper - executed a key has been encountered first time
            (v1, v2) -> v1 % 2 == 0 ? v1 + 1 : v1 + 2 // merging the values: if existing value is even adding `1`, otherwise `2`
        ));
}

main()

public static void main(String[] args) {
    System.out.println(oddEvenCount(Arrays.asList("na","na","na","uk","uk")));
}

输出:

{na=5, uk=3}

在线演示链接


2
投票

请注意,您的自定义计数方式所产生的计数总是比正常计数得到的结果的两倍小一倍。你基本上把每件事都算作两件事,除了第一件事。唯一的边缘情况是 0,两种计数方式产生相同的结果,但如果是 0,无论如何它都不会被放入映射中,所以我们实际上不需要处理它。

我们可以使用

Collectors.counting()
收集器从
collectingAndThen
计算所需的结果:

var frequency = streamer.stream().collect(Collectors.groupingBy(Function.identity(),
    Collectors.collectingAndThen(Collectors.counting(),
        normalCount -> normalCount * 2 - 1)
    ));

1
投票

除了@Sweeper的答案以获得预期的计数结果之外,如果您的问题还暗示如何自定义收集操作,您可以使用方法

Collector.of()

该方法接受一个供应商、一个

BiConsumer
作为累加器,以及一个
BinaryOperator
作为组合器。供应商只需提供存储结果的容器,在您的情况下为
Map<String, Long>
。累加器通过使用自定义频率逻辑放置或更新条目来填充容器。最后,组合器简单地合并并行执行的子结果,以防流作为并行流执行。

List<String> streamer = Arrays.asList("na", "na", "na", "uk", "uk");
Map<String, Long> map4 = streamer.stream()
        .collect(Collector.of(
                HashMap::new,
                (Map<String, Long> map, String s) -> {
                    if (!map.containsKey(s)) {
                        map.put(s, 1L);
                    } else {
                        map.computeIfPresent(s, (String key, Long val) -> val % 2 != 0 ? val + 2 : val + 1);
                    }
                },
                (Map<String, Long> map1, Map<String, Long> map2) -> {
                    for (Map.Entry<String, Long> entry : map2.entrySet()) {
                        map1.computeIfPresent(entry.getKey(), (key, val) -> val + entry.getValue() + 1);
                        map1.computeIfAbsent(entry.getKey(), key -> entry.getValue());
                    }
                    return map1;
                }
        ));

输出

{na=5, uk=3}
© www.soinside.com 2019 - 2024. All rights reserved.