2015-08-28 11 views
5

Ich habe eine Reihe von Eingang Strings in folgendem Format:Java 8 groupingby mit benutzerdefinierten Schlüsseln

typeA:code1, 
typeA:code2, 
typeA:code3, 
typeB:code4, 
typeB:code5, 
typeB:code6, 
typeC:code7, 
... 

und ich brauche ein Map<String, List<String>> mit der folgenden Struktur zu erhalten:

typeA, [code1, code2, code3] 
typeB, [code4, code5, code6] 
typeC, [code7, code8, ...] 

Der Haken ist, Um jeden Typ zu generieren, muss ich eine Funktion wie diese an jedem Eingangsstring aufrufen:

I ' Ich bin ziemlich sicher, dass Streams und Collectors das können, aber ich bemühe mich, die richtige Zauberformel zu finden, um das zu erreichen.

Antwort

8

Hier ist eine Möglichkeit, es zu tun (vorausgesetzt, die Klasse A genannt wird):

Map<String, List<String>> result = Stream.of(input) 
          .collect(groupingBy(A::getType, mapping(A::getValue, toList()))); 

Wenn Sie die Ausgabe sortiert Sie eine TreeMap anstelle des Standard-HashMap verwenden können:

.collect(groupingBy(A::getType, TreeMap::new, mapping(A::getValue, toList()))); 

Voll Beispiel:

public static void main(String[] args) { 
    String input[] = ("typeA:code1," + 
       "typeA:code2," + 
       "typeA:code3," + 
       "typeB:code4," + 
       "typeB:code5," + 
       "typeB:code6," + 
       "typeC:code7").split(","); 

    Map<String, List<String>> result = Stream.of(input) 
        .collect(groupingBy(A::getType, mapping(A::getValue, toList()))); 
    System.out.println(result); 
} 

public static String getType(String code) { 
    return code.split(":")[0]; 
} 
public static String getValue(String code) { 
    return code.split(":")[1]; 
} 
+0

Danke, das ist ziemlich nah an dem, was ich mit kam. Ich erkannte, dass der Typ des ersten Parameters in groupingBy ein Func ist, und dass ich meine getType-Funktion dort verwenden konnte. – endian

6

Der Code wird einfach, wenn man bedenkt, was Sie ausgelassen haben, tha t Sie den zweiten Teil des geteilten Zeichenfolge müssen auch:

Map<String, List<String>> result = Stream.of(input).map(s->s.split(":", 2)) 
    .collect(groupingBy(a->a[0], mapping(a->a[1], toList()))); 

(vorausgesetzt, Sie haben ein import static java.util.stream.Collectors.*;)

es ist nichts falsch mit einem String in ein Array aufteilen, hat die Implementierung sogar eine „schnelle "path" für den allgemeinen Fall, dass Sie sich mit einem einzelnen einfachen Zeichen aufteilen, anstatt einen regulären Ausdruck zu komplizieren.

+0

Danke, das ist auch ein guter Ansatz. – endian

2

Obwohl ich zu langsam war, hier ist ein MCVE zeigt, wie dies mit Collectors#groupingBy gelöst werden kann.

Es gibt offensichtlich verschiedene Optionen für die Definition von "Klassifikator" und "Mapper". Hier verwende ich einfach String#substring, um den Teil vor und nach der ":" zu finden.

import static java.util.stream.Collectors.groupingBy; 
import static java.util.stream.Collectors.mapping; 
import static java.util.stream.Collectors.toList; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.Map; 
import java.util.Map.Entry; 

public class GroupingBySubstringsTest 
{ 
    public static void main(String[] args) 
    { 
     List<String> strings = new ArrayList<String>(); 
     strings.add("typeA:code1"); 
     strings.add("typeA:code2"); 
     strings.add("typeA:code3"); 
     strings.add("typeB:code4"); 
     strings.add("typeB:code5"); 
     strings.add("typeB:code6"); 
     strings.add("typeC:code7"); 

     Map<String, List<String>> result = strings.stream().collect(
      groupingBy(s -> s.substring(0, s.indexOf(":")), 
       mapping(s -> s.substring(s.indexOf(":")+1), toList()))); 

     for (Entry<String, List<String>> entry : result.entrySet()) 
     { 
      System.out.println(entry); 
     } 
    } 
}