ฉันจะเปลี่ยนรายชื่อเป็นรายการใน Java 8 ได้อย่างไร


532

ถ้าฉันมีList<List<Object>>, ฉันจะเปลี่ยนสิ่งนั้นให้กลายเป็นList<Object>วัตถุที่มีวัตถุทั้งหมดในลำดับการทำซ้ำเดียวกันโดยใช้คุณสมบัติของ Java 8 ได้อย่างไร?

คำตอบ:


950

คุณสามารถใช้flatMapเพื่อทำให้รายการภายในเรียบ (หลังจากแปลงเป็นสตรีม) เป็นสตรีมเดียวแล้วรวบรวมผลลัพธ์ลงในรายการ:

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

11
@arshajii จริง แต่ด้วยเหตุผลบางอย่างฉันชอบการแสดงออกแลมบ์ดา บางทีผมอาจจะไม่ชอบรูปลักษณ์ของ:::)
Eran

25
Class::methodรู้สึกแปลก ๆ เล็กน้อยในตอนแรก แต่ก็มีประโยชน์ที่จะประกาศว่าคุณกำลังทำแผนที่วัตถุชนิดใด นั่นคือสิ่งที่คุณสูญเสียไปในลำธาร
ArneHugo

2
แต่คุณอาจต้องการมีคอนเทนเนอร์ของรายการและในกรณีนั้นคุณอาจต้องใช้แลมบ์ดา (l-> l.myList.stream ())
Myoch

2
หากคุณจำเป็นต้องทำการแคสต์อย่างชัดเจน (เช่นอาร์เรย์ของรายการดั้งเดิมไปยังรายการ) แลมบ์ดาอาจมีความจำเป็นเช่นกัน
Michael Fulton

52

flatmap ดีกว่า แต่มีวิธีอื่นในการบรรลุเป้าหมายเดียวกัน

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

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

37

flatMapวิธีการเกี่ยวกับการStreamได้อย่างแน่นอนแผ่รายการเหล่านั้นสำหรับคุณ แต่มันต้องสร้างStreamวัตถุสำหรับองค์ประกอบแล้วStreamสำหรับผล

คุณไม่ต้องการStreamวัตถุเหล่านั้นทั้งหมด นี่คือรหัสย่อที่กระชับและง่ายต่อการปฏิบัติงาน

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

เพราะListเป็นIterableรหัสนี้เรียกวิธี (Java 8 คุณลักษณะ) ซึ่งจะรับมาจาก forEachIterable

ดำเนินการกับการกระทำที่กำหนดสำหรับแต่ละองค์ประกอบของIterableจนกว่าองค์ประกอบทั้งหมดจะได้รับการประมวลผลหรือการกระทำที่เกิดข้อยกเว้น การดำเนินการจะดำเนินการตามลำดับของการวนซ้ำหากมีการระบุคำสั่งนั้น

และListของIteratorรายการสินค้าส่งคืนตามลำดับ

สำหรับConsumerรหัสนี้ส่งผ่านในการอ้างอิงวิธีการ (คุณสมบัติ Java 8) ไปยังวิธีการ pre-Java 8 List.addAllเพื่อเพิ่มองค์ประกอบรายการภายในตามลำดับ

ผนวกองค์ประกอบทั้งหมดในคอลเลกชันที่ระบุไปยังจุดสิ้นสุดของรายการนี้ในลำดับที่พวกเขาจะถูกส่งกลับโดย iterator ของคอลเลกชันที่ระบุ (การดำเนินการเสริม)


3
คำแนะนำทางเลือกที่ดีที่หลีกเลี่ยงการจัดสรรที่ไม่จำเป็น จะน่าสนใจที่จะเห็นการใช้ฮีปบนสุดเมื่อทำงานกับคอลเลกชันขนาดใหญ่เพื่อดูว่าพวกเขาเปรียบเทียบกันอย่างไร
ต่อ Lundberg

12

คุณสามารถใช้flatCollect()รูปแบบจากคราสคอลเลกชัน

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

หากคุณไม่สามารถเปลี่ยนรายการจากList:

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

หมายเหตุ: ฉันเป็นผู้บริจาคให้กับ Eclipse Collections


21
เหตุใดจึงต้องใช้การพึ่งพาบุคคลที่สามเมื่อ Java 8 มีฟังก์ชันการทำงานให้
saw303

2
Eclipse Collections API อยู่ในคอลเล็กชันดังนั้นโค้ดมีความกระชับเป็นหนึ่งในเหตุผลหลักในกรณีนี้
Nikhil Nanivadekar

11

เช่นเดียวกับ @Saravana พูดถึง:

flatmap นั้นดีกว่า แต่ก็มีวิธีอื่นในการบรรลุเป้าหมายเดียวกัน

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

ในการสรุปมีหลายวิธีในการบรรลุดังนี้:

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

ผมแค่อยากจะอธิบายสถานการณ์อย่างใดอย่างหนึ่งมากขึ้นเช่นList<Documents>รายการนี้มีรายการอื่น ๆ อีกไม่กี่เอกสารอื่น ๆ ที่ชอบList<Excel>, ,List<Word> List<PowerPoint>ดังนั้นโครงสร้างจึงเป็น

class A {
  List<Documents> documentList;
}

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

ตอนนี้ถ้าคุณต้องการทำซ้ำ Excel จากเอกสารเท่านั้นให้ทำสิ่งต่อไปนี้

ดังนั้นรหัสจะเป็น

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

ฉันหวังว่านี่จะช่วยแก้ปัญหาของใครบางคนในขณะที่เข้ารหัส ...


1

เราสามารถใช้ flatmap สำหรับสิ่งนี้ได้โปรดดูรหัสด้านล่าง:

 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

การขยายคำตอบของ Eran ซึ่งเป็นคำตอบสูงสุดถ้าคุณมีรายการหลายเลเยอร์คุณสามารถแบนแมปพวกเขาไว้ได้

สิ่งนี้ยังมาพร้อมกับวิธีการกรองที่สะดวกในขณะที่คุณลงเลเยอร์หากจำเป็นเช่นกัน

ตัวอย่างเช่น:

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

นี่จะคล้ายกันใน SQL เพื่อให้มีคำสั่ง SELECT ภายในคำสั่ง SELECT


1

วิธีการแปลงList<List>ไปList:

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

ดูตัวอย่างนี้:

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

}       

มันพิมพ์:

listOfLists => [[a, b, c]]
list => [a, b, c]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.