简体   繁体   English

Java 8流映射 <String, List<String> &gt;每个键的值总和

[英]Java 8 stream Map<String, List<String>> sum of values for each key

I am not so familiar with Java 8 (still learning) and looking to see if I could find something equivalent of the below code using streams. 我对Java 8(仍在学习)并不熟悉,并希望看看我是否可以使用流找到与下面代码相同的东西。

The below code mainly tries to get corresponding double value for each value in String and then sums it up. 下面的代码主要尝试为String中的每个值获取相应的double值,然后将其求和。 I could not find much help anywhere on this format. 在这种格式的任何地方我找不到多少帮助。 I am not sure if using streams would clean up the code or would make it messier. 我不确定使用流是否会清理代码或使其变得更加混乱。

// safe assumptions - String/List (Key/Value) cannot be null or empty
// inputMap --> Map<String, List<String>>

Map<String, Double> finalResult = new HashMap<>();
for (Map.Entry<String, List<String>> entry : inputMap.entrySet()) {
    Double score = 0.0;
    for (String current: entry.getValue()) {
        score += computeScore(current);
    }
    finalResult.put(entry.getKey(), score);
}

private Double computeScore(String a) { .. }
Map<String, Double> finalResult = inputMap.entrySet()
        .stream()
        .collect(Collectors.toMap(
                Entry::getKey,
                e -> e.getValue()
                      .stream()
                      .mapToDouble(str -> computeScore(str))
                      .sum()));

Above code iterates over the map and creates a new map with same keys & before putting the values, it first iterates over each value - which is a list, computes score via calling computeScore() over each list element and then sums the scores collected to be put in the value. 上面的代码遍历地图并创建一个具有相同键的新地图,在放置值之前,它首先迭代每个值 - 这是一个列表,通过调用每个列表元素上的computeScore()计算得分,然后将收集的得分相加被赋予价值。

You could also use the forEach method along with the stream API to yield the result you're seeking. 您还可以使用forEach方法和流API来生成您正在寻找的结果。

Map<String, Double> resultSet = new HashMap<>();
inputMap.forEach((k, v) -> resultSet.put(k, v.stream()
            .mapToDouble(s -> computeScore(s)).sum()));

s -> computeScore(s) could be changed to use a method reference ie T::computeScore where T is the name of the class containing computeScore . s -> computeScore(s)可以更改为使用方法引用,即T::computeScore ,其中T是包含computeScore的类的名称。

How about this one: 这个怎么样:

Map<String, Double> finalResult = inputMap.entrySet()
    .stream()
    .map(entry -> new AbstractMap.SimpleEntry<String, Double>(   // maps each key to a new
                                                                 // Entry<String, Double>
        entry.getKey(),                                          // the same key
        entry.getValue().stream()                             
            .mapToDouble(string -> computeScore(string)).sum())) // List<String> mapped to 
                                                                 // List<Double> and summed
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue));  // collected by the same 
                                                                 // key and a newly 
                                                                 // calulcated value

The version above could be merged to the single collect(..) method: 上面的版本可以合并到单个collect(..)方法:

Map<String, Double> finalResult = inputMap.entrySet()
    .stream()
    .collect(Collectors.toMap(
         Entry::getKey,                        // keeps the same key
         entry -> entry.getValue()
                       .stream()               // List<String> -> Stream<String>
                                               // then Stream<String> -> Stream<Double>
                       .mapToDouble(string -> computeScore(string)) 
                       .sum()));               // and summed 

The key parts: 关键部分:

  • collect(..) performs a reduction on the elements using a certain strategy with a Collector . collect(..)使用Collector使用特定策略对元素执行减少。
  • Entry::getKey is a shortcut for entry -> entry.getKey . Entry::getKeyentry -> entry.getKey的快捷方式。 A function for mapping the key. 用于映射密钥的函数。
  • entry -> entry.getValue().stream() returns the Stream<String> entry -> entry.getValue().stream()返回Stream<String>
  • mapToDouble(..) returns the DoubleStream. mapToDouble(..)返回DoubleStream。 This has an aggregating operation sum(..) which sums the elements - together creates a new value for the Map. 它有一个聚合操作sum(..) ,它对元素求和 - 一起为Map创建一个新值。

Regardless of whether you use the stream-based or the loop-based solution, it would be beneficial and add some clarity and structure to extract the inner loop into a method: 无论您使用基于流的解决方案还是基于循环的解决方案,都将是有益的,并添加一些清晰度和结构来将内部循环提取到方法中:

private double computeScore(Collection<String> strings) 
{
    return strings.stream().mapToDouble(this::computeScore).sum();
}

Of course, this could also be implemented using a loop, but ... that's exactly the point: This method can now be called, either in the outer loop, or on the values of a stream of map entries. 当然,这也可以使用循环来实现,但是......正是这一点:现在可以在外部循环中或在映射条目流的值上调用此方法。

The outer loop or stream could also be pulled into a method. 外环或流也可以被拉入方法中。 In the example below, I generalized this a bit: The type of the keys of the map does not matter. 在下面的例子中,我对此进行了概括:地图键的类型无关紧要。 Neither does whether the values are List or Collection instances. 值也不是ListCollection实例。

As an alternative to the currently accepted answer, the stream-based solution here does not fill a new map that is created manually. 作为当前接受的答案的替代方案,此处基于流的解决方案不会填充手动创建的新地图。 Instead, it uses a Collector . 相反,它使用Collector

(This is similar to other answers, but I think that the extracted computeScore method greatly simplifies the otherwise rather ugly lambdas that are necessary for the nested streams) (这与其他答案类似,但我认为提取的computeScore方法极大地简化了嵌套流所必需的相当丑陋的lambda)

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

public class ToStreamOrNotToStream
{
    public static void main(String[] args)
    {
        ToStreamOrNotToStream t = new ToStreamOrNotToStream();

        Map<String, List<String>> inputMap =
            new LinkedHashMap<String, List<String>>();
        inputMap.put("A", Arrays.asList("1.0", "2.0", "3.0"));
        inputMap.put("B", Arrays.asList("2.0", "3.0", "4.0"));
        inputMap.put("C", Arrays.asList("3.0", "4.0", "5.0"));

        System.out.println("Result A: " + t.computeA(inputMap));
        System.out.println("Result B: " + t.computeB(inputMap));
    }

    private <T> Map<T, Double> computeA(
        Map<T, ? extends Collection<String>> inputMap)
    {
        Map<T, Double> finalResult = new HashMap<>();
        for (Entry<T, ? extends Collection<String>> entry : inputMap.entrySet())
        {
            double score = computeScore(entry.getValue());
            finalResult.put(entry.getKey(), score);
        }
        return finalResult;
    }

    private <T> Map<T, Double> computeB(
        Map<T, ? extends Collection<String>> inputMap)
    {
        return inputMap.entrySet().stream().collect(
            Collectors.toMap(Entry::getKey, e -> computeScore(e.getValue()))); 
    }

    private double computeScore(Collection<String> strings) 
    {
        return strings.stream().mapToDouble(this::computeScore).sum();
    }

    private double computeScore(String a)
    {
        return Double.parseDouble(a);
    }

}

我发现它有点短:

value = startDates.entrySet().stream().mapToDouble(Entry::getValue).sum();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM