2014-05-02 5 views
8

Voglio imparare come utilizzare la sintassi di Java 8 con corsi d'acqua e ha un po 'bloccato.Java 8 raggruppamento da da uno-a-molti

E 'abbastanza facile da groupingBy quando si dispone di una chiave per ogni valore. Ma cosa succede se ho un elenco di chiavi per ogni valore e voglio comunque categorizzarle con groupingBy? Devo infrangerlo in diverse dichiarazioni o c'è forse un po 'di magia in streaming che può essere fatto per renderlo più semplice.

Questo è il codice di base:

List<Album> albums = new ArrayList<>(); 
Map<Artist, List<Album>> map = albums.stream().collect(Collectors.groupingBy(this::getArtist)); 

Funziona benissimo se v'è un solo artista per ogni album. Ma devo restituire una lista poiché un album può avere molti artisti. L'album e l'artista sono usati per l'illustrazione, naturalmente, ho dei tipi di mondo reale.

Probabilmente c'è una soluzione semplice ma non l'ho trovata da un po 'quindi sto chiamando al cervello collettivo che questo sito rappresenta per risolvilo. :) Una soluzione complessa è anche il benvenuto nel caso in cui non esista uno semplice.

In classe Album o come un metodo di utilità di prendere un album come argomento:

Artist getArtist(); // ok 

List<Artist> getArtist(); // Not ok, since we now have many "keys" for every Album 

Cheers, Mikael Grev

+0

... C'è probabilmente una soluzione semplice: 'Various Artists' ;-) – mcalex

risposta

13

Penso che si sta dopo Collectors.mapping che può essere passata come secondo argomento a groupingBy

esempio completo

import java.util.AbstractMap; 
import java.util.List; 
import java.util.Map; 

import static java.util.Arrays.asList; 
import static java.util.Map.Entry; 
import static java.util.stream.Collectors.*; 

public class SO { 

    public static void main(String... args) { 

     List<Album> albums = asList(
       new Album(
         asList(
           new Artist("bob"), 
           new Artist("tom") 
         ) 
       ), 
       new Album(asList(new Artist("bill"))) 
     ); 

     Map<Artist, List<Album>> x = albums.stream() 
       .flatMap(album -> album.getArtist().stream().map(artist -> pair(artist, album))) 
       .collect(groupingBy(Entry::getKey, mapping(Entry::getValue, toList()))); 

     x.entrySet().stream().forEach(System.out::println); 
    } 

    static class Artist { 
     private final String name; 

     Artist(String name) { 
      this.name = name; 
     } 

     public String toString() {return name;} 

    } 

    static class Album { 
     private List<Artist> artist; 

     Album(List<Artist> artist) { 
      this.artist = artist; 
     } 

     List<Artist> getArtist() { 
      return artist; 
     } 

    } 

    private static <T,U> AbstractMap.SimpleEntry<T,U> pair(T t, U u) { 
     return new AbstractMap.SimpleEntry<T,U>(t,u); 
    } 


} 
+0

Che velocità impressionante! Sembra funzionare (compilatore sais ok, non può ancora funzionare anche se a causa di refactoring.) sperava in una cosa semplice "involucro", ma a volte le cose che dovrebbero essere semplici non sono .. –

+0

copio-incollato a Eclipse e ho ottenuto l'errore del compilatore: "Tipo non corrispondente: non può convertire da Map > alla mappa >". –

0

Nel caso in cui qualcuno alla ricerca di un esempio di lavoro, il seguito dovrebbe essere utile.

import java.util.AbstractMap; 
import java.util.AbstractMap.SimpleEntry; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 
import java.util.Map; 
import java.util.stream.Collectors; 

public class GroupSubject { 
    public static void main(String[] args) { 

     List<ContentItem> items = Arrays.asList(new ContentItem("maths"), new ContentItem("science"), 
       new ContentItem("social"), new ContentItem("chemistry"), new ContentItem("maths")); 

     Map<String, List<ContentItem>> x = (Map<String, List<ContentItem>>) items.stream() 
       .flatMap(item -> item.getSubjects().stream().map(subject -> pair(subject, item))) 
       .collect(Collectors.groupingBy(e -> ((SimpleEntry<String, ContentItem>) e).getKey(), Collectors 
         .mapping(e -> ((SimpleEntry<String, ContentItem>) e).getValue(), Collectors.toList()))); 

     System.out.println(x); 

    } 

    private static <T, U> AbstractMap.SimpleEntry<T, U> pair(T t, U u) { 
     return new AbstractMap.SimpleEntry<T, U>(t, u); 
    } 

} 

class ContentItem { 

    private List<String> subjects = new ArrayList<String>(); 

    public ContentItem(String string) { 
     subjects.add(string); 
    } 

    public List<String> getSubjects() { 
     return subjects; 
    } 

    public void setSubjects(List<String> subjects) { 
     this.subjects = subjects; 
    } 

} 
0

Usando Guava's Multimap si può avere seguente codice:

Si prega di notare che, anche se si dispone di SetMultimap<Artist, Album> di conseguenza questo è equivalente del risultato desiderato di Map<Artist, List<Album>>.

Penso che questo sia un po 'più chiara;)

SetMultimap<Artist, Album> artistToAlmbums = HashMultimap.create(); 
albums.stream().forEach(album -> { 
    album.getArtist().forEach(artist -> artistToAlmbums.put(artist, album)); 
}); 
Problemi correlati