ถ้าฉันมีList<List<Object>>
, ฉันจะเปลี่ยนสิ่งนั้นให้กลายเป็นList<Object>
วัตถุที่มีวัตถุทั้งหมดในลำดับการทำซ้ำเดียวกันโดยใช้คุณสมบัติของ Java 8 ได้อย่างไร?
ถ้าฉันมีList<List<Object>>
, ฉันจะเปลี่ยนสิ่งนั้นให้กลายเป็นList<Object>
วัตถุที่มีวัตถุทั้งหมดในลำดับการทำซ้ำเดียวกันโดยใช้คุณสมบัติของ Java 8 ได้อย่างไร?
คำตอบ:
คุณสามารถใช้flatMap
เพื่อทำให้รายการภายในเรียบ (หลังจากแปลงเป็นสตรีม) เป็นสตรีมเดียวแล้วรวบรวมผลลัพธ์ลงในรายการ:
List<List<Object>> list = ...
List<Object> flat =
list.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
Class::method
รู้สึกแปลก ๆ เล็กน้อยในตอนแรก แต่ก็มีประโยชน์ที่จะประกาศว่าคุณกำลังทำแผนที่วัตถุชนิดใด นั่นคือสิ่งที่คุณสูญเสียไปในลำธาร
flatmap
ดีกว่า แต่มีวิธีอื่นในการบรรลุเป้าหมายเดียวกัน
List<List<Object>> listOfList = ... // fill
List<Object> collect =
listOfList.stream()
.collect(ArrayList::new, List::addAll, List::addAll);
flatMap
วิธีการเกี่ยวกับการStream
ได้อย่างแน่นอนแผ่รายการเหล่านั้นสำหรับคุณ แต่มันต้องสร้างStream
วัตถุสำหรับองค์ประกอบแล้วStream
สำหรับผล
คุณไม่ต้องการStream
วัตถุเหล่านั้นทั้งหมด นี่คือรหัสย่อที่กระชับและง่ายต่อการปฏิบัติงาน
// listOfLists is a List<List<Object>>.
List<Object> result = new ArrayList<>();
listOfLists.forEach(result::addAll);
เพราะList
เป็นIterable
รหัสนี้เรียกวิธี (Java 8 คุณลักษณะ) ซึ่งจะรับมาจาก forEach
Iterable
ดำเนินการกับการกระทำที่กำหนดสำหรับแต่ละองค์ประกอบของ
Iterable
จนกว่าองค์ประกอบทั้งหมดจะได้รับการประมวลผลหรือการกระทำที่เกิดข้อยกเว้น การดำเนินการจะดำเนินการตามลำดับของการวนซ้ำหากมีการระบุคำสั่งนั้น
และList
ของIterator
รายการสินค้าส่งคืนตามลำดับ
สำหรับConsumer
รหัสนี้ส่งผ่านในการอ้างอิงวิธีการ (คุณสมบัติ Java 8) ไปยังวิธีการ pre-Java 8 List.addAll
เพื่อเพิ่มองค์ประกอบรายการภายในตามลำดับ
ผนวกองค์ประกอบทั้งหมดในคอลเลกชันที่ระบุไปยังจุดสิ้นสุดของรายการนี้ในลำดับที่พวกเขาจะถูกส่งกลับโดย iterator ของคอลเลกชันที่ระบุ (การดำเนินการเสริม)
คุณสามารถใช้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
เช่นเดียวกับ @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);
}
ผมแค่อยากจะอธิบายสถานการณ์อย่างใดอย่างหนึ่งมากขึ้นเช่น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.
}
ฉันหวังว่านี่จะช่วยแก้ปัญหาของใครบางคนในขณะที่เข้ารหัส ...
เราสามารถใช้ 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);
การขยายคำตอบของ 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
วิธีการแปลง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]
::
:)