2017-12-07 27 views
5

Tôi thường thấy mình trong tình huống mà tôi cần phải tạo một đối tượng từ một Tập hợp hoặc List. Chìa khóa thường là một số chuỗi hoặc Enum hoặc tương tự, và giá trị là một số đối tượng mới với dữ liệu gộp lại với nhau. Cách thông thường để thực hiện điều này, một phần của tôi, trước tiên là tạo ra các Map<String, SomeKeyValueObject> và sau đó lặp qua Tập hợp hoặc Danh sách tôi nhận được và biến đổi bản đồ mới được tạo của tôi.Luồng Java: Thêm vào bản đồ nhưng tránh đột biến

Giống như ví dụ sau:

class Example { 
    Map<String, GroupedDataObject> groupData(final List<SomeData> list){ 
     final Map<String, GroupedDataObject> map = new HashMap<>(); 
     for(final SomeData data : list){ 
     final String key = data.valueToGroupBy(); 
     map.put(key, GroupedDataObject.of(map.get(key), data.displayName(), data.data())); 
     } 
     return map; 
    } 

} 

class SomeData { 
    private final String valueToGroupBy; 
    private final Object data; 
    private final String displayName; 

    public SomeData(final String valueToGroupBy, final String displayName, final Object data) { 
     this.valueToGroupBy = valueToGroupBy; 
     this.data = data; 
     this.displayName = displayName; 
    } 

    public String valueToGroupBy() { 
     return valueToGroupBy; 
    } 

    public Object data() { 
     return data; 
    } 

    public String displayName() { 
     return displayName; 
    } 
} 

class GroupedDataObject{ 

    private final String key; 
    private final List<Object> datas; 

    private GroupedDataObject(final String key, final List<Object> list) { 
     this.key = key; 
     this.datas = list; 
    } 

    public static GroupedDataObject of(final GroupedDataObject groupedDataObject, final String key, final Object data) { 
     final List<Object> list = new ArrayList<>(); 
     if(groupedDataObject != null){ 
     list.addAll(groupedDataObject.datas()); 
     } 
     list.add(data); 
     return new GroupedDataObject(key, list); 
    } 

    public String key() { 
     return key; 
    } 

    public List<Object> datas() { 
     return datas; 
    } 
} 

này cảm thấy rất ô uế. Chúng tôi tạo ra một bản đồ, và sau đó biến đổi nó nhiều lần.

Tôi đã thích sử dụng java 8s của Stream giây và tạo cấu trúc dữ liệu không thay đổi (hoặc đúng hơn, bạn không thấy đột biến). Vậy có cách nào để biến nhóm dữ liệu này thành thứ gì đó sử dụng cách tiếp cận khai báo thay vì cách bắt buộc không?

Tôi đã cố thực hiện đề xuất trong https://stackoverflow.com/a/34453814/3478016 nhưng dường như tôi đang gặp trở ngại. Sử dụng cách tiếp cận trong câu trả lời (gợi ý sử dụng Collectors.groupingByCollectors.mapping) Tôi có thể lấy dữ liệu được sắp xếp thành bản đồ. Nhưng tôi không thể nhóm "dữ liệu" thành một và cùng một đối tượng.

Có cách nào để thực hiện theo cách khai báo hay tôi bị mắc kẹt với mệnh lệnh?

+0

Có bạn chỉ muốn * cuối cùng * 'data.displayName()' và 'data.data()' cho mỗi 'data.valueToGroupBy()'? – Bohemian

+0

@Bohemian 'data.displayName()' là giống nhau cho mỗi 'data.valueToGroupBy()'. Trong bài toán hiện tại của tôi, 'valueToGroupBy' là bằng tiếng Anh, và' displayName' bằng tiếng Thụy Điển. Nhưng chúng giống nhau. – Chewtoy

+1

Điều đó không trả lời được câu hỏi của tôi. Có hoặc không: Bạn đang cố gắng chỉ đưa dữ liệu cuối cùng được xem cho mỗi khóa trong bản đồ đầu ra? – Bohemian

Trả lời

5

Bạn có thể sử dụng Collectors.toMap với chức năng hợp nhất thay vì Collectors.groupingBy.

Map<String, GroupedDataObject> map = 
    list.stream() 
     .collect(Collectors.toMap(SomeData::valueToGroupBy, 
            d -> { 
            List<Object> l = new ArrayList<>(); 
            l.add(d.data()); 
            return new GroupedDataObject(d.valueToGroupBy(), l); 
            }, 
            (g1,g2) -> { 
             g1.datas().addAll(g2.datas()); 
             return g1; 
            })); 

Phương thức khởi tạo GroupedDataObject phải có sẵn để hoạt động.

+1

Tôi không nghĩ rằng điều này sẽ biên dịch. –

+0

Điều này hoàn toàn giải quyết được vấn đề của tôi. Mặc dù dài hơn trong dòng, phá vỡ lambdas làm cho điều này nhiều hơn nữa có thể tái sử dụng. Tôi cũng thấy rằng hàm tạo không cần phải truy cập được. Bạn cũng có thể sử dụng các phương thức nhà máy tĩnh. – Chewtoy

+0

@Eran không phải như vậy. Nhưng với một chút viết lại của nó nó hoạt động. Một 'of' để lấy một' String' và 'Object', để sử dụng trong giá trị lambda và một' of' lấy một 'Chuỗi' và hai' Đặt 'để sử dụng trong hợp nhất lambda. – Chewtoy

1

Nếu bạn tránh GroupedDataObject và chỉ cần bản đồ có khóa và danh sách bạn có thể sử dụng Collectors.groupingBy mà bạn đã xem xét.

Collectors.groupingBy sẽ cho phép bạn làm điều này:

List<SomeObject> list = getSomeList(); 

Map<SomeKey, List<SomeObject>> = list.stream().collect(Collectors.groupingBy(SomeObject::getKeyMethod)); 

Điều này đòi hỏi phải có SomeKey triển khai đúng equalshashValue

1

Đôi khi con suối không phải là con đường để đi. Tôi tin rằng đây là một trong những lần đó.

Một refactoring ít sử dụng merge() mang đến cho bạn:

Map<String, MyTuple> groupData(final List<SomeData> list) { 
    Map<String, MyTuple> map = new HashMap<>(); 
    list.forEach(d -> map.merge(d.valueToGroupBy(), new MyTuple(data.displayName(), data.data()), 
     (a, b) -> {a.addAll(b.getDatas()); return a;}); 

Giả sử một lớp hợp lý để giữ công cụ của bạn:

class MyTuple { 
    String displayName; 
    List<Object> datas = new ArrayList<>(); 
    // getters plus constructor that takes 1 data and adds it to list 
} 
Các vấn đề liên quan