2014-05-02 25 views
8

Tôi muốn tìm hiểu cách sử dụng cú pháp Java 8 với các luồng và bị kẹt một chút.Nhóm Java 8 theo từ một đến nhiều

Thật dễ dàng để nhóm lại khi bạn có một khóa cho mọi giá trị. Nhưng nếu tôi có Danh sách khóa cho mọi giá trị và vẫn muốn phân loại chúng với groupingBy thì sao? Tôi có phải chia nó thành nhiều câu hay có thể là một phép thuật nhỏ có thể được thực hiện để làm cho nó đơn giản hơn.

Đây là mã cơ bản:

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

Nó hoạt động tuyệt vời nếu chỉ có một nghệ sĩ cho mỗi album. Nhưng tôi phải trả lại một Danh sách vì một Album có thể có nhiều Nghệ sĩ. Album và nghệ sĩ được sử dụng để minh hoạ tất nhiên, tôi có các loại thực thế giới ..

Có lẽ là một giải pháp đơn giản nhưng tôi đã không tìm thấy nó trong một thời gian vì vậy tôi kêu gọi các bộ não tập thể trang web này đại diện cho giải quyết nó. :) Một giải pháp phức tạp cũng được chào đón trong trường hợp một giải pháp đơn giản không tồn tại.

Trong lớp Album hoặc như là một phương pháp hữu ích chụp một album như là đối số:

Artist getArtist(); // ok 

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

Chúc mừng, Mikael Grev

+0

... Có lẽ là một giải pháp đơn giản: 'Various Artists' ;-) – mcalex

Trả lời

13

Tôi nghĩ rằng bạn là sau khi Collectors.mapping có thể được thông qua như là một cuộc tranh luận thứ hai để groupingBy

Hoàn dụ

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

gì tốc độ ấn tượng! Dường như nó hoạt động (trình biên dịch là ok, không thể chạy được tuy nhiên do tái cấu trúc.) Đã hy vọng cho một điều "quấn" đơn giản nhưng đôi khi những thứ đơn giản không phải là .. –

+0

Tôi đã sao chép nó vào Eclipse và tôi gặp lỗi trình biên dịch: "Loại không khớp: không thể chuyển đổi từ Bản đồ <Đối tượng, Danh sách > thành Bản đồ >". –

0

Trong trường hợp ai đó đang tìm kiếm một ví dụ làm việc, dưới đây sẽ hữu ích.

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

Sử dụng Guava's Multimap bạn có thể có đoạn mã sau:

Xin lưu ý rằng ngay cả khi bạn có SetMultimap<Artist, Album> kết quả là đây là tương đương với kết quả mong muốn của Map<Artist, List<Album>>.

Tôi nghĩ rằng đây là một chút rõ ràng hơn;)

SetMultimap<Artist, Album> artistToAlmbums = HashMultimap.create(); 
albums.stream().forEach(album -> { 
    album.getArtist().forEach(artist -> artistToAlmbums.put(artist, album)); 
}); 
Các vấn đề liên quan