2017-01-16 10 views
3

次のコードは機能し、読み込み可能ですが、必要ないはずのない中間的な操作があります。私は実際のコードがはるかに大きなプロセスの一部であるので、この単純化されたバージョンを書いています。Java 8ストリームコレクタ - 複数のバケットのオブジェクトを持つマップを作成するコレクタ

私はのWidgetを持っています。それぞれの名前と複数の種類(WidgetType列挙型の定数で示されます)。これらの複数の型はStream<WidgetType>として取得可能ですが、必要であれば他の型として返すことができます。 (様々な理由から、それはこれらが原因で、これらのウィジェットは、実際のコードで後に使用される方法のStream<WidgetType>として返されることが強く望まある。)

これらのウィジェットは、後に翻訳されるEnumMap<WidgetType, List<Widget>>に追加されEnumMap<WidgetType, Widget[]>

Widgetが単一WidgetTypeを持っていた場合、これは解決些細しかし、任意のウィジェットは、1種類以上を持っている可能性があるので、私はCollectors.groupingBy()方法(およびその過負荷)の構文で、すべて自分につまずくいでしょう。

コード例はここでも完全に機能し、私に必要な正確な結果が得られます。

class StackOverFlowExample { 

    private final Map<WidgetType, Widget[]> widgetMap = new EnumMap<>(WidgetType.class); 

    public static void main(String[] args) { new StackOverFlowExample(); } 

    StackOverFlowExample() { 
     Collection<Widget> widgetList = getWidgetsFromWhereverWidgetsComeFrom(); 

     { 
      final Map<WidgetType, List<Widget>> intermediateMap = new EnumMap<>(WidgetType.class); 
      widgetList.forEach(w -> 
        w.getWidgetTypes().forEach(wt -> { 
         intermediateMap.putIfAbsent(wt, new ArrayList<>()); 
         intermediateMap.get(wt).add(w); 
        }) 
      ); 
      intermediateMap.entrySet().forEach(e -> widgetMap.put(e.getKey(), e.getValue().toArray(new Widget[0]))); 
     } 

     Arrays.stream(WidgetType.values()).forEach(wt -> System.out.println(wt + ": " + Arrays.toString(widgetMap.get(wt)))); 
    } 

    private Collection<Widget> getWidgetsFromWhereverWidgetsComeFrom() { 
     return Arrays.asList(
       new Widget("1st", WidgetType.TYPE_A, WidgetType.TYPE_B), 
       new Widget("2nd", WidgetType.TYPE_A, WidgetType.TYPE_C), 
       new Widget("3rd", WidgetType.TYPE_A, WidgetType.TYPE_D), 
       new Widget("4th", WidgetType.TYPE_C, WidgetType.TYPE_D) 
     ); 
    } 

} 

この出力:完全性期すため

TYPE_A: [1st, 2nd, 3rd] 
TYPE_B: [1st] 
TYPE_C: [2nd, 4th] 
TYPE_D: [3rd, 4th] 

は、ここWidgetクラスとWidgetType列挙です:

class Widget { 

    private final String  name; 
    private final WidgetType[] widgetTypes; 

    Widget(String n, WidgetType ... wt) { name = n; widgetTypes = wt; } 

    public String getName() { return name; } 
    public Stream<WidgetType> getWidgetTypes() { return Arrays.stream(widgetTypes).distinct(); } 

    @Override public String toString() { return name; } 
} 

enum WidgetType { TYPE_A, TYPE_B, TYPE_C, TYPE_D } 

このロジックを実行するためのより良い方法上の任意のアイデアは歓迎されています。ありがとう!

答えて

4

IMHOの場合、キーはWidgetインスタンスをStream<Pair<WidgetType, Widget>>インスタンスに変換することです。これを取得したら、flatMapウィジェットのストリームを作成し、結果のストリームに収集することができます。もちろん、私たちはPairをJavaで持っていないので、代わりにAbstractMap.SimpleEntryを使用しなければなりません。

widgets.stream() 
     // Convert a stream of widgets to a stream of (type, widget) 
     .flatMap(w -> w.getTypes().map(t->new AbstractMap.SimpleEntry<>(t, w))) 
     // Grouping by the key, and do additional mapping to get the widget 
     .collect(groupingBy(e->e.getKey(), 
       mapping(e->e.getValue, 
         collectingAndThen(toList(), l->l.toArray(new Widget[0]))))); 

P.S.これはIntelliJの提案がメソッド参照でラムダを短縮しない場合です。

+3

これはまだ 'ウィジェット[]'へ '一覧'からのステップを必要とします。 'collectAndThen(toList()、l - > l.toArray(new Widget [0]))'を使うことができました。 –

+1

ありがとう、@JornVernee。編集されました。 –

+0

'Collectors.mapping()'は私が欠けていた部分です。 'collectingAndThen()'の解決策もボーナスです。 (私はかなりのステップを必要とすることにかなり辞職していた。)それを釘付けにした、ルーク。 1日中+1。感謝万円! :) – Darkly

1

これは少し畳み込まれていますが、同じ出力を生成しますが、必ずしも同じ順序である必要はありません。それはjava.util.stream.Collectors.*の静的インポートを使用します。私のマシンで

widgetMap = widgetList.stream() 
     .flatMap(w -> w.getWidgetTypes().map(t -> new AbstractMap.SimpleEntry<>(t, w))) 
     .collect(groupingBy(Map.Entry::getKey, collectingAndThen(mapping(Map.Entry::getValue, toSet()), s -> s.stream().toArray(Widget[]::new)))); 

出力:

TYPE_A: [1st, 3rd, 2nd] 
TYPE_B: [1st] 
TYPE_C: [2nd, 4th] 
TYPE_D: [3rd, 4th] 
関連する問題