การดึงรายการจาก java.util.stream.Stream ใน Java 8


443

ฉันกำลังเล่นกับ lambdas Java 8 เพื่อกรองคอลเลกชันได้อย่างง่ายดาย แต่ฉันไม่พบวิธีรัดกุมในการดึงผลลัพธ์เป็นรายการใหม่ภายในข้อความเดียวกัน นี่คือแนวทางที่รัดกุมที่สุดของฉัน:

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = new ArrayList<>();
sourceLongList.stream().filter(l -> l > 100).forEach(targetLongList::add);

ตัวอย่างบนอินเทอร์เน็ตไม่ได้ตอบคำถามของฉันเพราะพวกเขาหยุดโดยไม่สร้างรายการผลลัพธ์ใหม่ จะต้องมีวิธีที่กระชับกว่านี้ ฉันจะคาดหวังว่าStreamชั้นมีวิธีการเป็นtoList(), toSet()...

มีวิธีที่ตัวแปรtargetLongListสามารถกำหนดโดยตรงโดยบรรทัดที่สามหรือไม่?


7
ในกรณีที่คุณไม่ต้องการsourceLongListหลังจากนั้นมีCollection.removeIf(…)เพื่อความสะดวก
Holger

2
แล้วเรื่องนี้ล่ะ List<Long> targetLongList = sourceLongList.stream().collect(Collectors.toList());
leeyuiwah

คำตอบ:


609

สิ่งที่คุณทำอาจจะเป็นวิธีที่ง่ายที่สุดที่ให้การเข้าพักของสตรีมลำดับ-มิฉะนั้นคุณจะต้องใส่โทรไปตามลำดับ () forEachก่อน

[แก้ไขในภายหลัง: เหตุผลที่จำเป็นต้องใช้การเรียกไปยังซีเควนเชียล () เป็นรหัสที่มันยืน ( forEach(targetLongList::add)) จะมีชีวิตชีวาถ้ากระแสนั้นขนานกัน ถึงอย่างนั้นมันก็จะไม่บรรลุผลตามที่ตั้งใจไว้เช่นเดียวกับforEachnondeterministic อย่างชัดเจนแม้ในลำดับการประมวลผลองค์ประกอบจะไม่รับประกัน คุณจะต้องใช้forEachOrderedเพื่อให้แน่ใจว่าการสั่งซื้อที่ถูกต้อง ความตั้งใจของผู้ออกแบบ Stream API คือคุณจะใช้ตัวรวบรวมในสถานการณ์นี้ดังต่อไปนี้]

ทางเลือกคือ

targetLongList = sourceLongList.stream()
    .filter(l -> l > 100)
    .collect(Collectors.toList());

10
toListนอกจากนี้ผมคิดว่ารหัสนี้ได้รับเพียงเล็กน้อยสั้นชัดเจนและสวยถ้าคุณใช้การนำเข้าคงที่ของ static import java.util.stream.Collectors.toList;นี้จะกระทำโดยการวางต่อไปนี้ในหมู่การนำเข้าแฟ้ม: .collect(toList())จากนั้นการเรียกเก็บเพียงอ่าน
Lii

4
ใน Eclipse เป็นไปได้ที่จะทำให้ IDE เพิ่มการนำเข้าคงที่สำหรับวิธีการ นี้จะกระทำโดยการเพิ่มCollectorsระดับในการตั้งค่า -> Java -> แก้ไข -> เนื้อหา Assist -> รายการโปรด หลังจากนี้คุณจะต้องพิมพ์toLiที่ hit Ctr + Spaceเพื่อให้ IDE เติมtoListและเพิ่มการนำเข้าแบบคงที่
Lii

3
สิ่งหนึ่งที่ต้องจำไว้คือว่าIntStreamและอื่น ๆ เกือบ - แต่ไม่ค่อนข้างค่อนข้างStreamไม่มีcollect(Collector)วิธีการและคุณจะต้องโทรIntStream.boxed()เพื่อแปลงให้เป็นปกติStreamก่อน toArray()จากนั้นอีกครั้งบางทีคุณอาจต้องการเพียงแค่
บ๊อบกลายพันธุ์

ทำไมเราต้องใช้sequential()ก่อนforEachหรือใช้ 'forEachOrdered`
amarnath harish

1
@amarnathharish เนื่องจาก forEach ไม่รับประกันการดำเนินการตามลำดับสำหรับการสตรีมแบบขนาน JavaDoc กล่าวว่า "พฤติกรรมของการดำเนินการนี้คือ nondeterministic อย่างชัดเจนสำหรับท่อส่งกระแสคู่ขนานการดำเนินการนี้ไม่รับประกันว่าจะเคารพคำสั่งเผชิญหน้าของลำธาร (ประโยคแรกของคำพูดนี้จริง ๆ แล้วหมายความว่าคำสั่งไม่รับประกันสำหรับลำธารตามลำดับเช่นกันแม้ว่าในทางปฏิบัติจะได้รับการเก็บรักษาไว้)
Maurice Naftalin

183

Updated:

อีกวิธีคือการใช้Collectors.toList:

targetLongList = 
    sourceLongList.stream().
    filter(l -> l > 100).
    collect(Collectors.toList());

โซลูชันก่อนหน้า:

อีกวิธีคือการใช้Collectors.toCollection:

targetLongList = 
    sourceLongList.stream().
    filter(l -> l > 100).
    collect(Collectors.toCollection(ArrayList::new));

60
อย่างไรก็ตามนี่เป็นประโยชน์ถ้าคุณต้องการใช้งานรายการเฉพาะ
orbfish

1
แม้ว่า beeing แนะนำให้ใช้รหัสกับส่วนต่อประสานมีกรณีที่ชัดเจน (หนึ่งในนั้นคือ GWT) เมื่อคุณต้องใช้รหัสกับการใช้งานที่เป็นรูปธรรม (ยกเว้นกรณีที่คุณต้องการรวบรวมการใช้งานรายการทั้งหมด
Eduard Korenschi

3
อีกโปรสำหรับวิธีนี้จากCollectors::toListjavadoc: "ไม่มีการรับประกันกับประเภท, ความผันแปร, ความต่อเนื่องของอนุกรมหรือความปลอดภัยของเธรดของรายการที่ส่งคืนหากต้องการการควบคุมมากกว่ารายการที่ถูกส่งคืนให้ใช้toCollection(Supplier)"
Charles Wood

12

ฉันชอบใช้วิธี util ที่คืนค่าตัวสะสมArrayListเมื่อนั่นคือสิ่งที่ฉันต้องการ

ฉันคิดว่าการแก้ปัญหาโดยใช้Collectors.toCollection(ArrayList::new)เสียงดังเกินไปสำหรับการดำเนินการทั่วไป

ตัวอย่าง:

ArrayList<Long> result = sourceLongList.stream()
    .filter(l -> l > 100)
    .collect(toArrayList());

public static <T> Collector<T, ?, ArrayList<T>> toArrayList() {
    return Collectors.toCollection(ArrayList::new);
}

ด้วยคำตอบนี้ฉันต้องการแสดงให้เห็นถึงความเรียบง่ายในการสร้างและใช้นักสะสมที่กำหนดเองซึ่งโดยทั่วไปมีประโยชน์มาก


หากคุณประกาศผลเป็นรายการ <ยาว> คุณไม่จำเป็นต้องใช้วิธีการใช้ประโยชน์นี้ Collector.toList จะทำ นอกจากนี้การใช้คลาสที่ระบุแทนอินเทอร์เฟซก็คือกลิ่นรหัส
Lluis Martinez

1
@LluisMartinez: "Collector.toList จะทำ" : ไม่ไม่ใช่ในหลาย ๆ สถานการณ์ เพราะมันไม่ควรใช้toListถ้าคุณต้องการแก้ไขรายการในภายหลังในโปรแกรม toListเอกสารบอกว่านี่: "มีการค้ำประกันไม่อยู่กับชนิดของความไม่แน่นอน serializability หรือด้ายความปลอดภัยของรายการกลับดังนี้หากการควบคุมที่มากกว่ารายการที่ส่งคืนจะต้องใช้toCollection." . คำตอบของฉันแสดงให้เห็นถึงวิธีที่จะทำให้สะดวกขึ้นในกรณีทั่วไป
Lii

ถ้าคุณต้องการสร้าง ArrayList โดยเฉพาะมันก็โอเค
Lluis Martinez

5

ถ้าคุณมีอาร์เรย์ของวิทยาการคุณสามารถใช้คอลเลกชันแบบดั้งเดิมที่มีอยู่ในEclipse คอลเลกชัน

LongList sourceLongList = LongLists.mutable.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
LongList targetLongList = sourceLongList.select(l -> l > 100);

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

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = 
    ListAdapter.adapt(sourceLongList).select(l -> l > 100, new ArrayList<>());

ถ้าคุณต้องการใช้LongStream:

long[] sourceLongs = new long[]{1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L};
LongList targetList = 
    LongStream.of(sourceLongs)
    .filter(l -> l > 100)
    .collect(LongArrayList::new, LongArrayList::add, LongArrayList::addAll);

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


4

วิธีที่มีประสิทธิภาพมากขึ้นเล็กน้อย (หลีกเลี่ยงการสร้างรายการแหล่งที่มาและการยกเลิกการทำกล่องอัตโนมัติโดยตัวกรอง):

List<Long> targetLongList = LongStream.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L)
    .filter(l -> l > 100)
    .boxed()
    .collect(Collectors.toList());


1

หากคุณไม่ทราบใช้ห้องสมุดของบุคคลที่ 3 ของ AOL ไซคลอปส์ทำปฏิกิริยา lib (เปิดเผยผมมีส่วนร่วม) มีส่วนขยายสำหรับทุกJDK เก็บประเภทรวมทั้งรายการ อินเตอร์เฟส ListX ขยาย java.util.List และเพิ่มตัวดำเนินการที่มีประโยชน์จำนวนมากรวมถึงตัวกรอง

คุณสามารถเขียน -

ListX<Long> sourceLongList = ListX.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
ListX<Long> targetLongList = sourceLongList.filter(l -> l > 100);

ListX สามารถสร้างได้จาก List ที่มีอยู่ (ผ่าน ListX.fromIterable)


1

มีวิธีการรวบรวมอีกวิธีหนึ่งที่จัดทำโดยคลาส LongStream และในทำนองเดียวกันโดยคลาส IntStream และ DoubleStream ด้วย

<R> R collect(Supplier<R> supplier,
              ObjLongConsumer<R> accumulator,
              BiConsumer<R,R> combiner)

ดำเนินการลดที่ไม่แน่นอนในองค์ประกอบของกระแสนี้ การลดที่ไม่แน่นอนคือการที่ค่าที่ลดลงเป็นคอนเทนเนอร์ผลลัพธ์ที่ไม่แน่นอนเช่น ArrayList และองค์ประกอบจะถูกรวมเข้าด้วยกันโดยการอัพเดตสถานะของผลลัพธ์แทนการแทนที่ผลลัพธ์ สิ่งนี้สร้างผลลัพธ์ที่เทียบเท่ากับ:

R result = supplier.get();
  for (long element : this stream)
       accumulator.accept(result, element);
  return result;

เช่นเดียวกับการลด (ความยาว LongBinaryOperator) การดำเนินการรวบรวมสามารถขนานกันได้โดยไม่ต้องทำการซิงโครไนซ์เพิ่มเติม นี่เป็นการทำงานของเครื่อง

และตอบคำถามของคุณด้วยวิธีการรวบรวมนี้มีดังต่อไปนี้:

    LongStream.of(1L, 2L, 3L, 3L).filter(i -> i > 2)
    .collect(ArrayList::new, (list, value) -> list.add(value)
    , (list1, list2) -> list1.addAll(list2));

ด้านล่างนี้เป็นตัวแปรอ้างอิงวิธีซึ่งค่อนข้างฉลาด แต่มีบางอย่างที่ยุ่งยากในการเข้าใจ:

     LongStream.of(1L, 2L, 3L, 3L).filter(i -> i > 2)
    .collect(ArrayList::new, List::add , List::addAll);

ด้านล่างจะเป็นตัวแปร HashSet:

     LongStream.of(1L, 2L, 3L, 3).filter(i -> i > 2)
     .collect(HashSet::new, HashSet::add, HashSet::addAll);

ตัวแปรที่คล้ายกันของ LinkedList มีลักษณะดังนี้:

     LongStream.of(1L, 2L, 3L, 3L)
     .filter(i -> i > 2)
     .collect(LinkedList::new, LinkedList::add, LinkedList::addAll);

0

ในกรณีที่มีคน (เช่นฉัน) ออกมามองหาวิธีการจัดการกับวัตถุแทนประเภทดั้งเดิมแล้วใช้ mapToObj()

String ss = "An alternative way is to insert the following VM option before "
        + "the -vmargs option in the Eclipse shortcut properties(edit the "
        + "field Target inside the Shortcut tab):";

List<Character> ll = ss
                        .chars()
                        .mapToObj(c -> new Character((char) c))
                        .collect(Collectors.toList());

System.out.println("List type: " + ll.getClass());
System.out.println("Elem type: " + ll.get(0).getClass());
ll.stream().limit(50).forEach(System.out::print);

พิมพ์:

List type: class java.util.ArrayList
Elem type: class java.lang.Character
An alternative way is to insert the following VM o

0
String joined = 
                Stream.of(isRead?"read":"", isFlagged?"flagged":"", isActionRequired?"action":"", isHide?"hide":"")
                      .filter(s -> s != null && !s.isEmpty())
                      .collect(Collectors.joining(","));

0

นี่คือรหัสโดยAbacusUtil

LongStream.of(1, 10, 50, 80, 100, 120, 133, 333).filter(e -> e > 100).toList();

การเปิดเผยข้อมูล: ฉันเป็นผู้พัฒนา AbacusUtil


ฉันไม่พบวิธี toList ใด ๆ ที่มีอยู่ในคลาส LongStream คุณสามารถเรียกใช้รหัสนี้ได้หรือไม่
Vaneet Kataria

@VaneetKataria ลองcom.landawn.abacus.util.stream.LongStreamหรือLongStreamExใน AbacusUtil
user_3380739

0

คุณสามารถเขียนรหัสใหม่ได้ดังนี้:

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = sourceLongList.stream().filter(l -> l > 100).collect(Collectors.toList());

ขอบคุณสำหรับข้อมูลของคุณ อย่างไรก็ตามโปรดอธิบายสิ่งที่คุณเปลี่ยนไปและเกี่ยวข้องกับคำถามไปไกลแค่ไหน
B - rian

ที่นี่ก่อนอื่นฉันแปลง ArrayList เป็น Steam โดยใช้ตัวกรองฉันกรองข้อมูลที่ต้องการ ในที่สุดฉันก็ใช้วิธีการรวบรวมของ java 8 สตรีมเพื่อรวบรวมข้อมูลในรายการใหม่ที่เรียกว่าเป็น targetLongList
Pratik Pawar

0

ในการรวบรวมในรายการที่ไม่แน่นอน

targetList = sourceList.stream()
                       .filter(i -> i > 100) //apply filter
                       .collect(Collectors.toList());

ในการรวบรวมในรายการที่ไม่เปลี่ยนรูป:

targetList = sourceList.stream()
                       .filter(i -> i > 100) //apply filter
                       .collect(Collectors.toUnmodifiableList());

คำอธิบายของcollectจากJavaDoc :

ดำเนินการลดที่ไม่แน่นอนในองค์ประกอบของสตรีมนี้โดยใช้ Collector นักสะสมสรุปฟังก์ชั่นที่ใช้เป็นอาร์กิวเมนต์ในการรวบรวม (ซัพพลายเออร์, BiConsumer, BiConsumer) เพื่อให้สามารถใช้กลยุทธ์การเก็บรวบรวมและองค์ประกอบของการดำเนินการรวบรวมเช่นการจัดกลุ่มหลายระดับหรือการแบ่งพาร์ติชัน หากสตรีมเป็นแบบขนานและ Collector พร้อมกันและสตรีมไม่ได้เรียงลำดับหรือไม่ได้เรียงลำดับตัวสะสมจะทำการลดพร้อมกัน (ดูรายละเอียดเกี่ยวกับการสะสมพร้อมกัน)

นี่เป็นการทำงานของเครื่อง

เมื่อดำเนินการแบบขนานผลลัพธ์ระหว่างกลางหลายรายการอาจถูกทำให้เป็นอินสแตนซ์เติมและผสานเพื่อรักษาความแยกของโครงสร้างข้อมูลที่ไม่แน่นอน ดังนั้นแม้ว่าจะดำเนินการแบบขนานกับโครงสร้างข้อมูลที่ไม่ปลอดภัย (เช่น ArrayList) ไม่จำเป็นต้องมีการซิงโครไนซ์เพิ่มเติมสำหรับการลดแบบขนาน


-3

หากคุณไม่ได้ใช้parallel()สิ่งนี้จะทำงาน

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);

List<Long> targetLongList =  new ArrayList<Long>();

sourceLongList.stream().peek(i->targetLongList.add(i)).collect(Collectors.toList());

ฉันไม่ชอบที่ collect () ใช้เพื่อขับสตรีมเท่านั้นเพื่อให้ตะขอ peek () ถูกเรียกใช้ในแต่ละรายการ ผลลัพธ์ของการดำเนินการของเทอร์มินัลจะถูกยกเลิก
แดเนียลเค

มันแปลกมากที่จะโทร collectแล้วไม่บันทึกค่าส่งคืน ในกรณีนั้นคุณสามารถใช้forEachแทน แต่นั่นก็ยังเป็นทางออกที่ไม่ดี
Lii

1
ใช้ peek () ด้วยวิธีนี้เป็น antipattern
Grzegorz Piwowarek

ตามวิธีการสตรีม Java docs peek ต้องใช้เพื่อการดีบักเท่านั้นไม่ควรใช้สำหรับการประมวลผลอื่นใดนอกจากการดีบัก
Vaneet Kataria
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.