Javaの8ストリーム一覧<foo>の条件groupingByと地図<日、地図<文字列、ロング>>へ

XtremeBaumer:

以下のクラス:

public class Foo {
    private Date date;
    private String name;
    private Long number;
}

私は今持っているList<Foo>私はに変換したいMap<Date, Map<String,Long>>Longの合計でなければなりませんがnumbers)。これはハードになり、私は他の25より低い番号を持つすべてのものを合計26日を「その他」と呼ばれる内部マップで正確に26のエントリをしたいということです。

私は、次のコードを思い付きました:

data.stream().collect(Collectors.groupingBy(e -> e.getDate(), Collectors.groupingBy(e -> {
    if (/*get current size of inner map*/>= 25) {
        return e.getName();
    } else {
        return "Other";
    }

}, Collectors.summingLong(e -> e.getNumber()))));

あなたが見ることができるように、私は内側のマップに既にある要素の数を確認する方法は考えています。どのように私は、内部マップの現在のサイズを取得したり、私が欲しいものを達成するための別の方法はありますか?

私のJava 7のコード:

Map<Date, Map<String, Long>> result = new LinkedHashMap<Date, Map<String, Long>>();
for (Foo fr : data) {
    if (result.get(fr.getDate()) == null) {
        result.put(fr.getDate(), new LinkedHashMap<String, Long>());
    }
    if (result.get(fr.getDate()) != null) {
        if (result.get(fr.getDate()).size() >= 25) {
            if (result.get(fr.getDate()).get("Other") == null) {
                result.get(fr.getDate()).put("Other", 0l);
            }
            if (result.get(fr.getDate()).get("Other") != null) {
                long numbers= result.get(fr.getDate()).get("Other");
                result.get(fr.getDate()).replace("Other", numbers+ fr.getNumbers());
            }
        } else {
            result.get(fr.getDate()).put(fr.getName(), fr.getNumbers());
        }
    }
}

編集:

マップはこのようなテーブルを実現するために私を助けてください。

ここでは、画像の説明を入力します。

しかし、私は最初に「その他」を合計する必要があります。


あなたが必要な場合は、それ以上に関する情報は、お気軽に

ホルガー:

私は、この操作はストリームAPIを使用して恩恵を受けるだろうとは思いません。それでも、あなたは、Java 8の機能と操作性を向上することができます:

Map<Date, Map<String, Long>> result = new LinkedHashMap<>();
for(Foo fr : data) {
    Map<String, Long> inner
      = result.computeIfAbsent(fr.getDate(), date -> new LinkedHashMap<>());
    inner.merge(inner.size()>=25?"Other":fr.getAirlineName(), fr.getNumbers(), Long::sum);
}

このコードは、航空会社の名前はすでに各日付に対して一意であることを前提としています。そうしないと、あなたはにコードを拡張する必要があります

Map<Date, Map<String, Long>> result = new LinkedHashMap<>();
for(Foo fr : data) {
    Map<String, Long> inner
      = result.computeIfAbsent(fr.getDate(), date -> new LinkedHashMap<>());
    inner.merge(inner.size() >= 25 && !inner.containsKey(fr.getAirlineName())?
      "Other": fr.getAirlineName(), fr.getNumbers(), Long::sum);
}

正しく航空会社の値を蓄積します。


完全を期すため、ここではストリーム動作としてそれを実装する方法です。

カスタムコレクタは、いくつかの複雑さを持っているので、それは価値が再利用可能なコードとしてそれを書くことです。

public static <T,K,V> Collector<T,?,Map<K,V>> toMapWithLimit(
    Function<? super T, ? extends K> key, Function<? super T, ? extends V> value,
    int limit, K fallBack, BinaryOperator<V> merger) {

    return Collector.of(LinkedHashMap::new, (map, t) ->
            mergeWithLimit(map, key.apply(t), value.apply(t), limit, fallBack, merger),
            (map1,map2) -> {
                if(map1.isEmpty()) return map2;
                if(map1.size()+map2.size() < limit)
                    map2.forEach((k,v) -> map1.merge(k, v, merger));
                else
                    map2.forEach((k,v) ->
                        mergeWithLimit(map1, k, v, limit, fallBack, merger));
                return map1;
            });
}
private static <T,K,V> void mergeWithLimit(Map<K,V> map, K key, V value,
    int limit, K fallBack, BinaryOperator<V> merger) {
    map.merge(map.size() >= limit && !map.containsKey(key)? fallBack: key, value, merger);
}

これは似ているCollectors.toMapが、制限と追加項目のフォールバックキーをサポートしています。あなたは認識することがMap.merge重要な要素として、ループの解決に類似の呼び出しを、。

その後、あなたにコレクタを使用することができます

Map<Date, Map<String, Long>> result = data.stream().collect(
    Collectors.groupingBy(Foo::getDate, LinkedHashMap::new,
        toMapWithLimit(Foo::getAirlineName, Foo::getNumbers, 25, "Other", Long::sum)));

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=177871&siteId=1