2015-10-21 39 views
15

Tôi làm cách nào để làm như sau với Luồng Java?Luồng Java: nhóm một Danh sách thành Bản đồ của Bản đồ

Hãy nói rằng tôi có các lớp sau:

class Foo { 
    Bar b; 
} 

class Bar { 
    String id; 
    String date; 
} 

Tôi có một List<Foo> và tôi muốn chuyển nó sang một Map <Foo.b.id, Map<Foo.b.date, Foo>. I.e: nhóm trước tiên theo số Foo.b.id và sau đó là Foo.b.date.

Tôi đang phải vật lộn với cách tiếp cận 2 bước sau đây, nhưng điều thứ hai thậm chí không biên dịch:

Map<String, List<Foo>> groupById = 
     myList 
       .stream() 
       .collect(
         Collectors.groupingBy(
           foo -> foo.getBar().getId() 
         ) 
       ); 

Map<String, Map<String, Foo>> output = groupById.entrySet() 
     .stream() 
     .map(
       entry -> entry.getKey(), 
       entry -> entry.getValue() 
         .stream() 
         .collect(
           Collectors.groupingBy(
             bar -> bar.getDate() 
           ) 
         ) 
     ); 

Cảm ơn trước.

+1

Vâng, bạn có chắc chắn mọi phần tử trong danh sách sẽ là duy nhất không? Nghĩa là, một id và một ngày sẽ cho chính xác một đối tượng 'Foo' duy nhất? – RealSkeptic

+1

Bạn có muốn bản đồ 'Bản đồ ' hoặc 'Bản đồ >' ? – assylias

+0

@Eran bạn đúng, chỉnh sửa :) – mrod

Trả lời

22

Bạn có thể nhóm dữ liệu của bạn trong một lần giả định chỉ có riêng biệt Foo:

Map<String, Map<String, Foo>> map = list.stream() 
     .collect(Collectors.groupingBy(f -> f.b.id, 
       Collectors.toMap(f -> f.b.date, Function.identity()))); 

Lưu một số chara cters bằng cách sử dụng nhập khẩu tĩnh:

Map<String, Map<String, Foo>> map = list.stream() 
     .collect(groupingBy(f -> f.b.id, toMap(f -> f.b.date, identity()))); 
+1

Spot on, pal :) – mrod

2

Giả sử (b.id, b.date) cặp là riêng biệt. Nếu vậy, trong bước thứ hai bạn không cần nhóm, chỉ cần thu thập để Map nơi quan trọng là foo.b.date và giá trị là foo bản thân:

Map<String, Map<String, Foo>> map = 
     myList.stream() 
      .collect(Collectors.groupingBy(f -> f.b.id)) // map {Foo.b.id -> List<Foo>} 
      .entrySet().stream() 
      .collect(Collectors.toMap(e -> e.getKey(),     // id 
             e -> e.getValue().stream()  // stream of foos 
              .collect(Collectors.toMap(f -> f.b.date, 
                     f -> f)))); 

Hoặc thậm chí đơn giản hơn:

Map<String, Map<String, Foo>> map = 
     myList.stream() 
      .collect(Collectors.groupingBy(f -> f.b.id, 
              Collectors.toMap(f -> f.b.date, 
                  f -> f))); 
1

Một cách khác là để hỗ trợ các hợp đồng bình đẳng trên chìa khóa của bạn, Bar:

class Bar { 
    String id; 
    String date; 

    public boolean equals(Object o){ 
     if (o == null) return false; 
     if (!o.getClass().equals(getClass())) return false; 
     Bar other = (Bar)o; 
     return Objects.equals(o.id, id) && Objects.equals(o.date, date); 
    } 

    public int hashCode(){ 
     return id.hashCode*31 + date.hashCode; 
    }  
} 

Bây giờ bạn chỉ có thể có một Map<Bar, Foo>.

+0

OP chắc chắn muốn 'Bar' với cùng một 'id' nhưng khác nhau' ngày' cũng được lưu trữ, nhưng trong cách tiếp cận của bạn sẽ bị vứt bỏ. Một lỗ hổng khác trong mã của bạn là cách so sánh không an toàn bằng null so với 'id' - 'Objects.equals()' là tốt hơn cho điều này. Và tôi không thể hiểu mục đích của việc so sánh 'o.date' với' this.id' là gì? –

+0

@SashaSalauyou Câu lệnh đầu tiên không đúng, bạn có thể có cùng id và ngày khác nhau bằng cách sử dụng phương thức này. Và như cho nulls, Map không cho phép các khóa null, vì vậy tôi cho rằng chúng không có. Điểm thứ ba là lỗi đánh máy, có thể ảnh hưởng đến điểm đầu tiên! – weston

+0

Có, bản chỉnh sửa của bạn được thực hiện bình đẳng dựa trên 'id' và' date', không phải là 'id'. Bây giờ để tìm kiếm 'Foo' bạn cần tạo đối tượng' Bar' giả - chắc chắn nó có thể là một cách tiếp cận. +1 –

Các vấn đề liên quan