2016-06-28 5 views
2

Ich möchte folgendeJava Streams verwandeln Karte von EnumMap in eine Karte von Listen

enum Parameter {Foo, Bar, Baz}; 

Map<String, EnumMap<Parameter, String>> myMap = new HashMap(){{ 
    put("KEY1", new EnumMap<>(Parameter.class) {{ 
     put(Parameter.Foo, "BAD"); 
     put(Parameter.Bar, "GOOD"); 
     put(Parameter.Baz, "BAD"); 
    }}); 
    put("KEY2", new EnumMap<>(Parameter.class) {{ 
     put(Parameter.Foo, "BAD"); 
     put(Parameter.Bar, "GOOD"); 
     put(Parameter.Baz, "GOOD"); 
    }}); 
}}; 

in ein gefiltertes Karte umwandeln, wo der Filter nur entweder die

Map<String, List<Entry<Parameter, String>>> mapList 

Wo die resultierende Liste enthält sollte die Zeilen mit "GOOD" als Filterkriterium enthalten.

Ich kann nicht die richtige Stromsyntax herauszufinden, dies zu tun, ich weiß, sie entlang der folgenden Zeilen:

Map<String, List<Entry<Parameter, String>>> mapList = 
    myMap.entrySet().stream().groupingBy(e -> e.getKey()).collect(Collectors.toMap(????) 
+3

ich fragen müssen, warum hast du so gekünstelt Code beginnen mit? Eine Karte einer EnumMap und Sie möchten eine Karte der Liste der Karteneinträge erstellen? Wäre es nicht besser, richtige Objekte zu haben? – Tunaki

+0

Die eigentlichen Daten stammen aus einer CSV-Datei, die vom Dienst auf der obersten Ebene organisiert wird. In jedem Dienst sind die EnumMap-Parameter tatsächlich Zuordnungen von festen Gerätetypen zu den entsprechenden IP-Adressfolgen, also z. Die CSV ist fest und nicht in separate Tabellen aufgeteilt. Auf diese Weise ist der obige (e -> e.getKey()) immer noch derselbe Service-Name-Schlüssel auf hoher Ebene (die Servicenamen sind KEY1 & KEY2 in meinem erfundenen Beispiel) – johnco3

+1

Sicher, aber es wäre viel einfacher, eine Zeile zu haben Ihrer CSV werden durch einen tatsächlichen Typ dargestellt. Erstellen Sie eine Klasse 'Service' mit einem Namen und einer Liste von _things_ (benötigt einen besseren Namen), die den Gerätetyp, die Adresse usw. enthalten. – Tunaki

Antwort

2

Die Einträge bereits richtig gruppiert sind, nur die Werte auspackte und gefiltert werden müssen:

import static java.util.stream.Collectors.*; 
... 
Map<String, List<Entry<Parameter, String>>> mapList = new HashMap<>(); 

myMap.forEach((k, v) -> { 
    List<Entry<Parameter, String>> filtered = v.entrySet().stream() 
    .filter(e -> e.getValue().equals("GOOD")) 
    .collect(toList()); 

    if(!filtered.isEmpty()) mapList.put(k, filtered); 
}); 
+0

Sehr nett, nur ein kleines Problem, in meinen tatsächlichen Daten habe ich einige ganze Zeilen in myMap, die keine übereinstimmenden Einträge haben - das obige wird einen Eintrag in mapList mit leeren Werten haben, wie würde ich diese Einträge herausfiltern - verzeih mir Newbie Fragen, wie ich bin neu in den Streams api – johnco3

+0

Ich fand heraus, wie nur nicht leere Werte durch Überprüfung der v.entrySet(). stream(). Filter (...), In meinem Fall stellt sich heraus, dass forEach sein ((k, v) -> {Liste > ports = v.entrySet(). stream(). filter (e ->! e.getValue(). equals ("0:00")) .collect (Collectors.toList()); if (! ports.isEmpty()) portInfo.put (k, validPorts);}); - yeuk aber es funktioniert – johnco3

+1

@ johnco3 Ich war im Begriff, das gleiche vorzuschlagen. Ich war auf der Suche nach einem besseren Weg, aber ich denke, das ist das Beste. –

3

Es ist nicht klar, warum Sie die EnumMap Natur des Wertes betonen, da die Logik nicht ändert, wenn dieser spezifischen Map-Typen. Sie haben eine Karte, deren Werte Sie konvertieren möchten, die Schlüssel beibehalten, was bedeutet, dass das Streaming über die Einträge und das Sammeln über Collectors.toMap der richtige Weg ist. Lassen Sie die Schlüsselfunktion den ursprünglichen Schlüssel und die Wertfunktion die eigentliche Konvertierung ausführen.

Die Werte passieren ein Map sein, die Sie zu einem List<Map.Entry> konvertieren wollen, was recht einfach ist, die Einträge Streaming über und sie zu einem List zu sammeln. Das Hinzufügen eines Filters, der nur "GOOD" Werte akzeptiert, ist trivial.

Nun, es ist schade, dass Sie buried another important requirement in a comment statt es in der Frage angeben. Sie möchten die Schlüssel der äußeren Karte auslassen, wenn in ihrer Zielkarte überhaupt kein "GOOD" Wert vorhanden ist. So benötigen Sie einen Filter für den Außenstrom Operation, die nicht so schwer ist, wenn Sie sich erinnern, wie stark die Sammlung API ist, auch ohne Streams:

Map<String, List<Map.Entry<Parameter, String>>> mapList 
    = myMap.entrySet().stream() 
     .filter(e -> e.getValue().containsValue("GOOD")) // need at least one GOOD there 
     .collect(Collectors.toMap(
      Map.Entry::getKey, // keep original keys 
      e -> e.getValue().entrySet().stream() // actual conversion 
       .filter(sub -> sub.getValue().equals("GOOD")) // GOOD values only 
       .collect(Collectors.toList()) // make List<Map.Entry> 
      ));