web-dev-qa-db-ja.com

Java 8ストリームMap <String、List <String >>各キーの値の合計

Java 8(まだ学習中))にあまり馴染みがなく、ストリームを使用して以下のコードに相当するものを見つけることができるかどうかを確認しています。

以下のコードは主に、Stringの各値に対応するdouble値を取得してから合計しようとします。この形式では、どこにも助けが見つかりませんでした。ストリームを使用するとコードがクリーンアップされるのか、それとも面倒になるのかわかりません。

// 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) { .. }
12
meso

ストリームAPIとともにforEachメソッドを使用して、探している結果を生成することもできます。

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

s -> computeScore(s)は、メソッド参照、つまり_T::computeScore_を使用するように変更できます。ここで、TcomputeScoreを含むクラスの名前です。

8
Aomine
_Map<String, Double> finalResult = inputMap.entrySet()
        .stream()
        .collect(Collectors.toMap(
                Entry::getKey,
                e -> e.getValue()
                      .stream()
                      .mapToDouble(str -> computeScore(str))
                      .sum()));
_

上記のコードはマップを反復処理し、同じキーで新しいマップを作成します。値を入力する前に、まず各値を反復処理します。これはリストであり、各リスト要素に対してcomputeScore()値に入れるために収集されたスコア。

11
Pankaj Singhal

これはどう:

_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
_

上記のバージョンは、単一の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 
_

重要な部分:

  • collect(..) は、 Collector を使用して特定の戦略を使用して要素の削減を実行します。
  • _Entry::getKey_は_entry -> entry.getKey_のショートカットです。キーをマッピングするための関数。
  • entry -> entry.getValue().stream()は_Stream<String>_を返します
  • mapToDouble(..) は、DoubleStreamを返します。これには、要素を合計する集約操作 sum(..) があり、一緒にマップの新しい値を作成します。
3
Nikolas

ストリームベースのソリューションを使用するか、ループベースのソリューションを使用するかに関係なく、内部ループをメソッドに抽出するために有益であり、明確さと構造を追加します。

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

もちろん、これはループを使用して実装することもできますが、...それがまさにポイントです。このメソッドは、外側のループまたはマップエントリのストリームの値で呼び出すことができます。

外側のループまたはストリームもメソッドにプルできます。以下の例では、これを少し一般化しました。マップのキーのタイプは重要ではありません。値がListまたはCollectionインスタンスのどちらでもありません。

現在受け入れられている答えの代替として、ここでのストリームベースのソリューションは、手動で作成された新しいマップを埋めません。代わりに、Collectorを使用します。

(これは他の回答と似ていますが、抽出されたcomputeScoreメソッドは、ネストされたストリームに必要な、そうでなければかなりいラムダを大幅に簡素化すると思います)

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

}
0
Marco13

私はそれがやや短いことがわかりました:

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