2015-08-31 44 views
6

Tôi có danh sách Lớp A, bao gồm Danh sách chính nó.Làm cách nào để mở rộng và tập hợp lại Danh sách Danh sách bằng cách sử dụng Java 8 Stream?

public class A { 
    public double val; 
    public String id; 
    public List<String> names = new ArrayList<String>(); 
    public A(double v, String ID, String name) 
    { 
     val = v; 
     id = ID; 
     names.add(name); 
    } 

static public List<A> createAnExample() 
{ 
    List<A> items = new ArrayList<A>(); 

    items.add(new A(8.0,"x1","y11")); 
    items.add(new A(12.0, "x2", "y21")); 
    items.add(new A(24.0,"x3","y31")); 
    items.get(0).names.add("y12"); 
    items.get(1).names.add("y11"); 
    items.get(1).names.add("y31"); 
    items.get(2).names.add("y11"); 
    items.get(2).names.add("y32"); 
    items.get(2).names.add("y33"); 
    return items; 
} 

Mục đích là tổng giá trị trung bình trên mỗi id trong Danh sách. Tôi đã thêm mã trong chức năng chính bằng cách sử dụng một số luồng Java 8. Câu hỏi của tôi là làm thế nào tôi có thể viết lại nó một cách thanh lịch hơn mà không cần sử dụng Array thứ hai và vòng lặp for.

static public void main(String[] args) { 
    List<A> items = createAnExample(); 

    List<A> items2 = new ArrayList<A>(); 
    for (int i = 0; i < items.size(); i++) { 
     List<String> names = items.get(i).names; 
     double v = items.get(i).val/names.size(); 
     String itemid = items.get(i).id; 
     for (String n : names) { 
      A item = new A(v, itemid, n); 
      items2.add(item); 
     } 
    } 
    Map<String, Double> x = items2.stream().collect(Collectors.groupingBy(item -> 
      item.names.isEmpty() ? "NULL" : item.names.get(0), Collectors.summingDouble(item -> item.val))); 
    for (Map.Entry entry : x.entrySet()) 
     System.out.println(entry.getKey() + " --> " + entry.getValue()); 
} 

Trả lời

4

Bạn có thể làm điều đó với flatMap:

x = items.stream() 
    .flatMap(a -> a.names.stream() 
     .map(n -> new AbstractMap.SimpleEntry<>(n, a.val/a.names.size())) 
    ).collect(groupingBy(
     Map.Entry::getKey, summingDouble(Map.Entry::getValue) 
    )); 

Nếu bạn thấy mình đối phó với những vấn đề như thế này thường xuyên, hãy xem xét một phương pháp tĩnh để tạo ra một Map.Entry:

static<K,V> Map.Entry<K,V> entry(K k, V v) { 
    return new AbstractMap.SimpleImmutableEntry<>(k,v); 
} 

Sau đó, bạn sẽ có ít chi tiết hơn .map(n -> entry(n, a.val/a.names.size()))

1

Trong m y miễn phí StreamEx thư viện mở rộng API luồng chuẩn có các hoạt động đặc biệt giúp xây dựng các bản đồ phức tạp như vậy. Sử dụng StreamEx vấn đề của bạn có thể được giải quyết như thế này:

Map<String, Double> x = StreamEx.of(createAnExample()) 
    .mapToEntry(item -> item.names, item -> item.val/item.names.size()) 
    .flatMapKeys(List::stream) 
    .grouping(Collectors.summingDouble(v -> v)); 

Đây mapToEntry tạo dòng các mục bản đồ (cái gọi là EntryStream), nơi các phím là danh sách các tên và giá trị là Vals trung bình. Tiếp theo, chúng tôi sử dụng flatMapKeys để làm phẳng các phím để lại các giá trị như cũ (vì vậy chúng tôi có luồng Entry<String, Double>). Cuối cùng, chúng tôi nhóm chúng lại với nhau để tổng hợp các giá trị cho các phím lặp lại.

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