Bagaimana saya bisa mengubah Daftar Daftar menjadi Daftar di Java 8?

534

Jika saya punya List<List<Object>>, bagaimana saya bisa mengubahnya menjadi List<Object>yang berisi semua objek dalam urutan iterasi yang sama dengan menggunakan fitur Java 8?

Sarah Szabo
sumber

Jawaban:

952

Anda bisa menggunakan flatMapuntuk meratakan daftar internal (setelah mengubahnya menjadi Streaming) menjadi satu Stream, dan kemudian mengumpulkan hasilnya menjadi daftar:

List<List<Object>> list = ...
List<Object> flat = 
    list.stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());
Eran
sumber
11
@arshajii benar, tetapi untuk beberapa alasan saya lebih suka ekspresi lambda. Mungkin saya tidak suka tampilan :::)
Eran
25
Class::methodterasa sedikit aneh pada awalnya, tetapi memiliki manfaat bahwa ia menyatakan jenis objek yang Anda pemetaan. Jika tidak demikian, itu adalah sesuatu yang Anda kehilangan dalam aliran.
ArneHugo
2
Namun Anda mungkin ingin memiliki wadah Daftar dan dalam hal ini Anda mungkin perlu menggunakan lambda (l-> l.myList.stream ()).
Myoch
2
Jika Anda perlu melakukan casting eksplisit (array primitif ke Daftar misalnya) maka lambdas mungkin diperlukan juga.
Michael Fulton
52

flatmap lebih baik tetapi ada cara lain untuk mencapai hal yang sama

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

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

The flatMapmetode pada Streamkaleng tentu meratakan daftar tersebut untuk Anda, tetapi harus membuat Streamobjek untuk elemen, makaStream untuk hasil.

Anda tidak perlu semua Streambenda itu. Berikut adalah kode ringkas dan ringkas untuk melakukan tugas tersebut.

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

Karena Listini Iterable, kode ini panggilan yang forEachmetode (Java 8 fitur), yang diwarisi dari Iterable.

Lakukan tindakan yang diberikan untuk setiap elemen Iterablehingga semua elemen diproses atau tindakan melempar pengecualian. Tindakan dilakukan dalam urutan iterasi, jika urutan itu ditentukan.

Dan List's Iteratorkembali item secara berurutan.

Untuk Consumer, kode ini meneruskan referensi metode (fitur Java 8) ke metode pra-Java 8 List.addAlluntuk menambahkan elemen daftar bagian dalam secara berurutan.

Tambahkan semua elemen dalam koleksi yang ditentukan ke akhir daftar ini, dalam urutan bahwa mereka dikembalikan oleh iterator koleksi yang ditentukan (operasi opsional).

rgettman
sumber
3
Saran alternatif yang baik yang menghindari beberapa alokasi yang tidak perlu. Akan menarik untuk melihat penggunaan tumpukan atas ini ketika bekerja dengan beberapa koleksi yang lebih besar, untuk melihat bagaimana mereka membandingkan satu sama lain.
Per Lundberg
12

Anda dapat menggunakan flatCollect()pola dari Eclipse Collections .

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

Jika Anda tidak dapat mengubah daftar dari List:

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

Catatan: Saya adalah kontributor untuk Eclipse Collections.

Nikhil Nanivadekar
sumber
21
mengapa menggunakan dependensi pihak ketiga ketika fungsionalitas disediakan oleh Java 8?
saw303
2
Eclipse Collections API ada di koleksi itu sendiri, jadi kodenya ringkas, adalah salah satu alasan utama dalam kasus ini.
Nikhil Nanivadekar
11

Seperti yang disebutkan oleh @Saravana:

flatmap lebih baik tetapi ada cara lain untuk mencapai hal yang sama

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

Singkatnya, ada beberapa cara untuk mencapai hal yang sama sebagai berikut:

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);
}
Dengarkan
sumber
11

Saya hanya ingin menjelaskan satu skenario lebih seperti List<Documents>, daftar ini berisi daftar yang lebih beberapa dokumen lain seperti List<Excel>, List<Word>, List<PowerPoint>. Jadi strukturnya

class A {
  List<Documents> documentList;
}

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

Sekarang jika Anda ingin mengulangi Excel hanya dari dokumen kemudian lakukan sesuatu seperti di bawah ini ..

Jadi kodenya

 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.
 }

Saya harap ini dapat memecahkan masalah seseorang saat ...

Kushwaha
sumber
1

Kami dapat menggunakan flatmap untuk ini, silakan lihat kode di bawah ini:

 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);
Pratik Pawar
sumber
1

Perluasan jawaban Eran yang merupakan jawaban teratas, jika Anda memiliki banyak lapisan daftar, Anda dapat terus memetakannya.

Ini juga dilengkapi dengan cara pemfilteran yang praktis saat Anda membuka lapisan jika diperlukan juga.

Jadi misalnya:

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())

Ini akan serupa dalam SQL untuk memiliki pernyataan SELECT dalam pernyataan SELECT.

cody.tv.weber
sumber
1

Metode untuk mengonversi a List<List>menjadi List:

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

Lihat contoh ini:

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);
    }

}       

Mencetak:

listOfLists => [[a, b, c]]
list => [a, b, c]
Soudipta Dutta
sumber