Làm cách nào tôi có thể biến Danh sách Danh sách thành Danh sách trong Java 8?


532

Nếu tôi có một List<List<Object>>, làm thế nào tôi có thể biến nó thành một List<Object>đối tượng chứa tất cả các đối tượng theo cùng một thứ tự lặp bằng cách sử dụng các tính năng của Java 8?

Câu trả lời:


950

Bạn có thể sử dụng flatMapđể làm phẳng các danh sách nội bộ (sau khi chuyển đổi chúng thành Luồng) thành một Luồng duy nhất và sau đó thu thập kết quả vào một danh sách:

List<List<Object>> list = ...
List<Object> flat = 
    list.stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());

11
@arshajii đúng, nhưng vì một số lý do tôi thích biểu thức lambda. Có lẽ tôi không thích vẻ ngoài của :::)
Eran

25
Class::methodban đầu cảm thấy hơi kỳ lạ, nhưng nó có lợi ích là nó tuyên bố loại đối tượng bạn đang ánh xạ từ đâu. Đó là một cái gì đó bạn mất trong dòng.
ArneHugo

2
Tuy nhiên, bạn có thể muốn có một danh sách và trong trường hợp đó bạn có thể cần sử dụng lambda (l-> l.myList.stream ()).
Myoch

2
Nếu bạn cần thực hiện đúc rõ ràng (ví dụ mảng nguyên thủy cho Danh sách) thì lambdas cũng có thể cần thiết.
Michael Fulton

52

flatmap tốt hơn nhưng có nhiều cách khác để đạt được điều tương tự

List<List<Object>> listOfList = ... // fill

List<Object> collect = 
      listOfList.stream()
                .collect(ArrayList::new, List::addAll, List::addAll);

37

Các flatMapphương pháp trên Streamlon chắc chắn làm phẳng những danh sách cho bạn, nhưng nó phải tạo Streamđối tượng cho các phần tử, sau đó một Streamkết quả.

Bạn không cần tất cả những Streamđối tượng đó. Dưới đây là mã đơn giản, súc tích để thực hiện nhiệm vụ.

// listOfLists is a List<List<Object>>.
List<Object> result = new ArrayList<>();
listOfLists.forEach(result::addAll);

Bởi vì một ListIterable, mã này gọi là các forEachphương pháp (Java 8 tính năng), được thừa hưởng từ Iterable.

Thực hiện hành động đã cho cho từng phần tử của Iterablecho đến khi tất cả các phần tử đã được xử lý hoặc hành động đưa ra một ngoại lệ. Các hành động được thực hiện theo thứ tự lặp, nếu thứ tự đó được chỉ định.

Và một List's Iteratormục lợi nhuận theo thứ tự tuần tự.

Đối với Consumer, mã này chuyển một tham chiếu phương thức (tính năng Java 8) sang phương thức tiền Java 8 List.addAllđể thêm các phần tử danh sách bên trong một cách tuần tự.

Nối tất cả các phần tử trong bộ sưu tập đã chỉ định vào cuối danh sách này, theo thứ tự chúng được trả về bởi trình lặp của bộ sưu tập đã chỉ định (thao tác tùy chọn).


3
Đề nghị thay thế tốt mà tránh một số phân bổ không cần thiết. Sẽ rất thú vị khi thấy cách sử dụng heap hàng đầu của điều này khi làm việc với một số bộ sưu tập lớn hơn, để xem cách chúng so sánh với nhau.
Per Lundberg

12

Bạn có thể sử dụng flatCollect()mẫu từ Bộ sưu tập Eclipse .

MutableList<List<Object>> list = Lists.mutable.empty();
MutableList<Object> flat = list.flatCollect(each -> each);

Nếu bạn không thể thay đổi danh sách từ List:

List<List<Object>> list = new ArrayList<>();
List<Object> flat = ListAdapter.adapt(list).flatCollect(each -> each);

Lưu ý: Tôi là người đóng góp cho Bộ sưu tập Eclipse.


21
tại sao sử dụng sự phụ thuộc của bên thứ ba khi chức năng được cung cấp bởi Java 8?
cưa303

2
API bộ sưu tập Eclipse nằm trên chính bộ sưu tập, vì vậy mã này ngắn gọn, là một trong những lý do chính trong trường hợp này.
Nikhil Nanivadekar

11

Giống như @Saravana đã đề cập:

Flatmap là tốt hơn nhưng có nhiều cách khác để đạt được điều tương tự

 listStream.reduce(new ArrayList<>(), (l1, l2) -> {
        l1.addAll(l2);
        return l1;
 });

Tóm lại, có một số cách để đạt được như sau:

private <T> List<T> mergeOne(Stream<List<T>> listStream) {
    return listStream.flatMap(List::stream).collect(toList());
}

private <T> List<T> mergeTwo(Stream<List<T>> listStream) {
    List<T> result = new ArrayList<>();
    listStream.forEach(result::addAll);
    return result;
}

private <T> List<T> mergeThree(Stream<List<T>> listStream) {
    return listStream.reduce(new ArrayList<>(), (l1, l2) -> {
        l1.addAll(l2);
        return l1;
    });
}

private <T> List<T> mergeFour(Stream<List<T>> listStream) {
    return listStream.reduce((l1, l2) -> {
        List<T> l = new ArrayList<>(l1);
        l.addAll(l2);
        return l;
    }).orElse(new ArrayList<>());
}

private <T> List<T> mergeFive(Stream<List<T>> listStream) {
    return listStream.collect(ArrayList::new, List::addAll, List::addAll);
}

11

Tôi chỉ muốn giải thích một kịch bản giống như List<Documents>, danh sách này có chứa một vài danh sách chi tiết của các tài liệu khác như List<Excel>, List<Word>, List<PowerPoint>. Vì vậy, cấu trúc là

class A {
  List<Documents> documentList;
}

class Documents {
  List<Excel> excels;
  List<Word> words;
  List<PowerPoint> ppt;
}

Bây giờ nếu bạn chỉ muốn lặp lại Excel từ các tài liệu thì hãy làm một cái gì đó như dưới đây ..

Vì vậy, mã sẽ là

 List<Documents> documentList = new A().getDocumentList();

 //check documentList as not null

 Optional<Excel> excelOptional = documentList.stream()
                         .map(doc -> doc.getExcel())
                         .flatMap(List::stream).findFirst();
 if(excelOptional.isPresent()){
   Excel exl = optionalExcel.get();
   // now get the value what you want.
 }

Tôi hy vọng điều này có thể giải quyết vấn đề của ai đó trong khi mã hóa ...


1

Chúng tôi có thể sử dụng bản đồ phẳng cho việc này, vui lòng tham khảo mã dưới đây:

 List<Integer> i1= Arrays.asList(1, 2, 3, 4);
 List<Integer> i2= Arrays.asList(5, 6, 7, 8);

 List<List<Integer>> ii= Arrays.asList(i1, i2);
 System.out.println("List<List<Integer>>"+ii);
 List<Integer> flat=ii.stream().flatMap(l-> l.stream()).collect(Collectors.toList());
 System.out.println("Flattened to List<Integer>"+flat);

1

Một bản mở rộng về câu trả lời của Eran là câu trả lời hàng đầu, nếu bạn có một loạt các lớp danh sách, bạn có thể giữ sơ đồ hóa chúng.

Điều này cũng đi kèm với một cách lọc tiện dụng khi bạn đi xuống các lớp nếu cần thiết.

Ví dụ:

List<List<List<List<List<List<Object>>>>>> multiLayeredList = ...

List<Object> objectList = multiLayeredList
    .stream()
    .flatmap(someList1 -> someList1
        .stream()
        .filter(...Optional...))
    .flatmap(someList2 -> someList2
        .stream()
        .filter(...Optional...))
    .flatmap(someList3 -> someList3
        .stream()
        .filter(...Optional...))
    ...
    .collect(Collectors.toList())

Điều này sẽ tương tự trong SQL khi có các câu lệnh CHỌN trong các câu lệnh CHỌN.


1

Phương pháp chuyển đổi List<List>thành List:

listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());

Xem ví dụ này:

public class Example {

    public static void main(String[] args) {
        List<List<String>> listOfLists = Collections.singletonList(Arrays.asList("a", "b", "v"));
        List<String> list = listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());

        System.out.println("listOfLists => " + listOfLists);
        System.out.println("list => " + list);
    }

}       

Nó in:

listOfLists => [[a, b, c]]
list => [a, b, c]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.