แปลง Iterable เป็น Stream โดยใช้ Java 8 JDK


418

java.lang.Iterable<T>ฉันมีอินเตอร์เฟซที่ส่งกลับ

ฉันต้องการจัดการผลลัพธ์นั้นโดยใช้ Java 8 Stream API

อย่างไรก็ตาม Iterable ไม่สามารถ "สตรีม" ได้

แนวคิดใดที่จะใช้ Iterable เป็นสตรีมโดยไม่ต้องแปลงเป็น List?


2
หากคุณสามารถทำซ้ำได้ทำไมไม่ลองใช้การวนซ้ำเพื่อตรวจสอบสภาพหรือค่าของมัน
Afzaal Ahmad Zeeshan

26
@AfzaalAhmadZeeshan เพราะกระแสดีกว่ามาก
Sleiman Jneidi

1
ดังที่ฉันได้กล่าวไปแล้วฉันต้องทำบางรายการในรายการนั้น (ตัวกรองการจับคู่) ฉันต้องการใช้ Java 8 JDK API ใหม่ -> สตรีม แต่ Iterable ไม่ได้เป็น "SteamAble"
rayman

ดูเหมือนว่าแปลกที่myIterable.stream()ไม่มีอยู่!
Josh M.

7
@Guillaume: ใช่ แต่ผลิตStream.of(iterable) Stream<Iterable<Object>>
Matthias Braun

คำตอบ:


571

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

StreamSupport.stream(iterable.spliterator(), false)
             .filter(...)
             .moreStreamOps(...);

อย่างที่คุณเห็นการรับกระแสจากIterable(ดูคำถามนี้ด้วย ) นั้นไม่เจ็บปวดมาก


109
วิธีการคงที่จะได้รับดีเช่นStream Stream.ofIterable(iterable)
robinst

59
@robinst นี่ไม่ใช่การละเว้น มันเป็นทางเลือกโดยเจตนา (และถกเถียงอย่างมาก) ความท้าทายคือสิ่งนี้มีอยู่มันจะง่ายเกินกว่าจะเข้าถึงได้โดยไม่เข้าใจถึงการแลกเปลี่ยนที่มากับมัน เนื่องจาก Iterable มีความเป็นนามธรรมดังนั้นวิธีการดังกล่าวจะส่งผลให้สตรีมที่มีประสิทธิภาพแย่ที่สุดเท่าที่จะเป็นไปได้ (ไม่มีการสนับสนุนแบบขนานไม่มีข้อมูลขนาดหรือคุณสมบัติ บังคับให้คิดมากกว่านี้เพื่อให้มี API ที่ดีขึ้นทั่วทั้งระบบนิเวศ นี่เป็นการแลกเปลี่ยน "สิ่งที่ดีที่สุดสำหรับรหัส XYZ" vs "สิ่งที่ดีที่สุดสำหรับรหัส Java ทั้งหมด"
Brian Goetz

2
จากคำอธิบายของคุณฉันสงสัยว่าทำไมเราถึงได้ Collection.stream () แต่ไม่ใช่ Iterable.stream () ดูเหมือนว่าเหตุผลที่จะละเว้น Iterable.stream () (หรือ Stream.ofIterable ()) ใช้กับ Collection.stream () อย่างเท่าเทียมกัน
รอบคัดเลือก


11
@BrianGoetz ดูเหมือนว่าคนส่วนใหญ่ไม่ได้อยู่ในระดับที่จะเข้าใจว่าคุณพูดไปแล้วหรือไม่สนใจ พวกเขาเพียงต้องการเขียนโค้ดอย่างง่ายโดยเรียกใช้ API อย่างง่าย ในทางกลับกันสิ่งเหล่านั้น (ขนาน ... ) อาจไม่สำคัญสำหรับการดำเนินการ iterable เกือบทุกวัน
user_3380739

71

หากคุณสามารถใช้ห้องสมุด Guava ตั้งแต่รุ่น 21 คุณสามารถใช้

Streams.stream(iterable)

2
Lists.newArrayList(Iterable)หรือถ้าคุณกำลังติดอยู่ที่รุ่นเก่าใช้
Jacob van Lingen

1
การใช้ Guava ปัจจุบันไม่เลวร้ายยิ่งกว่าคำตอบที่ยอมรับ: github.com/google/guava/blob/master/guava/src/com/google/common/
Vadzim

23

คุณสามารถสร้างรายการStreamจากIterableหรือIterator:

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}

2
คุณต้องเขียนฟังก์ชั่นนี้หนึ่งครั้งแล้วเรียกมันว่า ทำไมการโทรเพื่อstream(...)ถ่วงรหัสของคุณ?
gexicide

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

ฟังก์ชั่นกล่าวว่าการนำเข้าคงที่ สั้นและสง่างาม ( แต่ไม่จำเป็นต้องโปร่งใส)
aepurniet

9

ฉันอยากจะแนะนำให้ใช้ห้องสมุดJOOLมันซ่อนเวทย์มนตร์ spliterator ไว้เบื้องหลังการเรียก Seq.seq (iterable) และยังมีฟังก์ชันการใช้งานที่เป็นประโยชน์เพิ่มเติมอีกมากมาย


7

ดังนั้นเป็นคำตอบที่กล่าวถึง Guava อื่นได้รับการสนับสนุนโดยใช้:

Streams.stream(iterable);

ฉันต้องการเน้นว่าการใช้งานทำบางสิ่งที่แตกต่างเล็กน้อยจากคำตอบอื่น ๆ ที่แนะนำ หากIterableเป็นประเภทที่Collectionพวกเขาโยนมัน

public static <T> Stream<T> stream(Iterable<T> iterable) {
  return (iterable instanceof Collection)
    ? ((Collection<T>) iterable).stream()
    : StreamSupport.stream(iterable.spliterator(), false);
}

public static <T> Stream<T> stream(Iterator<T> iterator) {
  return StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(iterator, 0),
    false
  );
}

5

ฉันได้สร้างคลาสนี้:

public class Streams {
    /**
     * Converts Iterable to stream
     */
    public static <T> Stream<T>  streamOf(final Iterable<T> iterable) {
        return toStream(iterable, false);
    }

    /**
     * Converts Iterable to parallel stream
     */
    public static <T> Stream<T> parallelStreamOf(final Iterable<T> iterable) {
        return toStream(iterable, true);
    }

    private static <T> Stream<T> toStream(final Iterable<T> iterable, final boolean isParallel) {
        return StreamSupport.stream(iterable.spliterator(), isParallel);
    }
}

ฉันคิดว่ามันอ่านได้อย่างสมบูรณ์แบบเพราะคุณไม่ต้องคิดถึง spliterators และ booleans (isParallel)


4

วิธีแก้ไขปัญหาที่ง่ายมากสำหรับปัญหานี้คือการสร้างStreamable<T>ส่วนต่อขยายIterable<T>ที่เก็บdefault <T> stream()วิธีการไว้

interface Streamable<T> extends Iterable<T> {
    default Stream<T> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
}

ตอนนี้ใด ๆ ที่คุณIterable<T>s สามารถทำนิด ๆ streamable เพียงด้วยการประกาศให้พวกเขาแทนimplements Streamable<T>Iterable<T>


0

หากคุณเคยใช้ Vavr (ชื่อเดิมคือ Javaslang) สิ่งนี้สามารถทำได้ง่ายเหมือน:

Iterable i = //...
Stream.ofAll(i);

-1

อีกวิธีในการทำด้วย Java 8 และไม่มี libs ภายนอก:

Stream.concat(collectionA.stream(), collectionB.stream())
      .collect(Collectors.toList())
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.