2015-08-23 16 views
6

Voglio prendere un set di oggetti (ObjectInstance in questo caso) e desidero raggrupparli per una proprietà e disporre gli elenchi risultanti in un altro.Come raggruppare un insieme di oggetti in liste ordinate usando java 8?

Set<ObjectInstance> beans = server.queryMBeans(null, null); 
Map<String, List<String>> beansByDomain = beans.stream() 
      .collect(groupingBy((ObjectInstance oi) -> oi.getObjectName().getDomain(), 
           mapping((ObjectInstance oi) -> oi.getObjectName().getCanonicalKeyPropertyListString(), 
           toList()))); 

L'espressione di cui sopra crea la struttura di dati corretto: un Map in cui le chiavi sono i domini dei ObjectInstance oggetti, ed i valori sono elenchi di liste di proprietà. Quello che voglio è ora ordinare le liste, per assicurarsi che siano in ordine alfabetico. C'è un modo per farlo nella stessa espressione?

Un'idea sarebbe quella di aggiungere .sort() subito dopo .stream(), ma è davvero garantito che funzioni?

risposta

5

Uso collectingAndThen:

List<String> beansByDomain = beans.stream() 
     .collect(groupingBy((ObjectInstance oi) -> oi.getObjectName().getDomain(), 
          mapping((ObjectInstance oi) -> oi.getObjectName().getCanonicalKeyPropertyListString(), 
          collectingAndThen(toList(), (l -> l.stream().sorted().collect(toList())))))); 

È possibile estrarre il collettore per rendere il codice più leggibile:

public static <T> Collector<T,?,List<T>> toSortedList() { 
    return Collectors.collectingAndThen(Collectors.toList(), 
             l -> l.stream().sorted().collect(toList())); 
} 

List<String> beansByDomain = beans.stream() 
     .collect(groupingBy((ObjectInstance oi) -> oi.getObjectName().getDomain(), 
          mapping((ObjectInstance oi) -> oi.getObjectName().getCanonicalKeyPropertyListString(), 
            toSortedList()))); 
+0

Sì, la seconda soluzione sembra molto migliore dal punto di vista della leggibilità. – KumarM

2

Sicuramente è possibile ordinare l'intero flusso prima di raccogliere:

Map<String, List<String>> beansByDomain = beans.stream() 
     .map(ObjectInstance::getObjectName) 
     .sorted(Comparator.comparing(ObjectName::getCanonicalKeyPropertyListString)) 
     .collect(groupingBy(ObjectName::getDomain, 
          mapping(ObjectName::getCanonicalKeyPropertyListString, 
          toList()))); 

Si noti che ho aggiunto il passaggio .map(ObjectInstance::getObjectName) in quanto non è necessario altro da ObjectInstance. Ciò funzionerà bene, anche se non posso prevedere se è più veloce di ordinare ogni lista risultante separatamente o meno.

Se si preferisce la separata toSortingList() collettore (come in @JeanLogeart risposta), ma può essere ottimizzato in questo modo:

public static <T extends Comparable<T>> Collector<T,?,List<T>> toSortedList() { 
    return collectingAndThen(toCollection(ArrayList::new), 
        (List<T> l) -> {l.sort(Comparator.naturalOrder()); return l;}); 
} 

Qui abbiamo esplicitamente raccogliamo per ArrayList (toList() fa lo stesso, ma non è garantito) , quindi ordinare l'elenco risultante sul posto senza copiare ulteriormente (utilizzando stream().sorted().collect(toList()) si copia l'intero contenuto dell'elenco almeno due volte). Si noti inoltre che il parametro <T> deve essere dichiarato come extends Comparable<T>. Altrimenti puoi usare erroneamente questo raccoglitore per un tipo non comparabile che si compila bene, ma si traduce in un errore di runtime.

+0

Anche a me piace questa soluzione. Avrei dovuto pensare alla mappatura di ObjectName, rende tutto più pulito. Ieri è stata una lunga giornata ... –

Problemi correlati