การใช้ Java 8 เป็นตัวเลือกกับ Stream :: flatMap


240

เฟรมเวิร์ก Java 8 ใหม่และเพื่อน ๆ สร้างขึ้นมาเพื่อโค้ดจาวาที่รัดกุม แต่ฉันได้เจอกับสถานการณ์ที่เรียบง่ายซึ่งดูเหมือนจะยุ่งยาก

พิจารณาและวิธีการList<Thing> things Optional<Other> resolve(Thing thing)ผมต้องการที่จะทำแผนที่Thingเพื่อOptional<Other>s Otherและได้รับเป็นครั้งแรก วิธีแก้ปัญหาที่ชัดเจนคือการใช้things.stream().flatMap(this::resolve).findFirst()แต่flatMapต้องการให้คุณส่งกระแสข้อมูลและOptionalไม่มีstream()วิธี (หรือเป็นCollectionหรือให้วิธีการแปลงเป็นหรือดูเป็นCollection)

สิ่งที่ดีที่สุดที่ฉันสามารถทำได้คือ:

things.stream()
    .map(this::resolve)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();

แต่ดูเหมือนว่าจะยืดยาวไปมากสำหรับสิ่งที่ดูเหมือนว่าเป็นกรณีที่พบบ่อยมาก ใครมีความคิดที่ดีกว่า


หลังจากเขียนโค้ดด้วยตัวอย่างของคุณแล้วฉันพบว่ารุ่นที่ชัดเจนนั้นสามารถอ่านได้มากกว่ารุ่นที่เกี่ยวข้องถ้ามีอยู่.flatMap(Optional::toStream)ด้วยรุ่นของคุณคุณจะเห็นสิ่งที่เกิดขึ้นจริง
skiwi

19
@skiwi อืมOptional.streamมีอยู่ใน JDK 9 ตอนนี้ ....
Stuart Marks

ฉันอยากรู้ว่าเอกสารนี้อยู่ที่ไหนและกระบวนการในการรับเข้าคืออะไร มีวิธีอื่น ๆ ที่ดูเหมือนว่าควรจะมีอยู่จริง ๆ และฉันอยากรู้ว่าการสนทนาเกี่ยวกับการเปลี่ยนแปลง API นั้นเกิดขึ้นที่ไหน
Yona Appletree


10
สิ่งที่ตลกคือ JDK-8050820 จริง ๆ แล้วอ้างอิงถึงคำถามนี้ในคำอธิบาย!
Didier L

คำตอบ:


265

Java 9

Optional.stream ได้รับการเพิ่มใน JDK 9 สิ่งนี้ช่วยให้คุณสามารถทำสิ่งต่อไปนี้โดยไม่จำเป็นต้องใช้วิธีการช่วยเหลือใด ๆ :

Optional<Other> result =
    things.stream()
          .map(this::resolve)
          .flatMap(Optional::stream)
          .findFirst();

Java 8

ใช่นี้เป็นรูเล็ก ๆ ใน API ได้ในการที่จะไม่สะดวกค่อนข้างที่จะเปิดเป็นศูนย์หรือหนึ่งยาวOptional<T> Stream<T>คุณสามารถทำได้:

Optional<Other> result =
    things.stream()
          .map(this::resolve)
          .flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
          .findFirst();

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

/**
 * Turns an Optional<T> into a Stream<T> of length zero or one depending upon
 * whether a value is present.
 */
static <T> Stream<T> streamopt(Optional<T> opt) {
    if (opt.isPresent())
        return Stream.of(opt.get());
    else
        return Stream.empty();
}

Optional<Other> result =
    things.stream()
          .flatMap(t -> streamopt(resolve(t)))
          .findFirst();

ที่นี่ฉันได้เรียกการเรียกไปที่resolve()แทนที่จะมีการmap()ดำเนินการแยกต่างหากแต่นี่เป็นเรื่องของรสนิยม


2
ฉันไม่คิดว่า api สามารถเปลี่ยนแปลงได้จนกว่า Java 9 ตอนนี้
assylias

5
@Hypher ขอบคุณ เทคนิค. ฟิลเตอร์ (). map () ไม่เลวร้ายนักและหลีกเลี่ยงการพึ่งพาวิธีการช่วยเหลือ 'คงจะดีถ้ามีวิธีรัดกุมมากกว่านี้ ฉันจะตรวจสอบว่าได้รับตัวเลือกเสริม. ()
Stuart Marks

43
ฉันชอบ:static <T> Stream<T> streamopt(Optional<T> opt) { return opt.map(Stream::of).orElse(Stream.empty()); }
kubek2k

5
ฉันหวังว่าพวกเขาจะเพิ่มการOptionalโอเวอร์โหลดในStream#flatMap... วิธีที่คุณสามารถเขียนได้stream().flatMap(this::resolve)
สะเก็ด

4
@flkes ใช่เราได้เตะรอบความคิดนี้ แต่ก็ดูเหมือนจะไม่เพิ่มสิ่งที่คุ้มค่ามากในขณะนี้ว่า (ใน JDK 9) Optional.stream()มี
Stuart Marks

69

ฉันเพิ่มนี้คำตอบที่สองขึ้นอยู่กับการแก้ไขที่เสนอโดยผู้ใช้srborlonganเพื่อคำตอบอื่น ๆ ของฉัน ฉันคิดว่าเทคนิคที่เสนอนั้นน่าสนใจ แต่มันก็ไม่เหมาะกับการแก้ไขคำตอบของฉัน คนอื่น ๆ เห็นด้วยและการแก้ไขที่เสนอได้รับการลงคะแนน (ฉันไม่ได้เป็นหนึ่งในผู้ลงคะแนน) เทคนิคนี้มีข้อดี คงจะดีที่สุดถ้า srborlongan โพสต์คำตอบของตนเอง สิ่งนี้ยังไม่เกิดขึ้นและฉันไม่ต้องการให้เทคนิคหายไปในหมอกของ StackOverflow ที่ปฏิเสธประวัติการแก้ไขดังนั้นฉันจึงตัดสินใจแยกมันออกเป็นคำตอบแยกต่างหาก

โดยทั่วไปแล้วเทคนิคคือการใช้Optionalวิธีการบางอย่างในวิธีที่ฉลาดเพื่อหลีกเลี่ยงการใช้ตัวดำเนินการแบบไตรภาค ( ? :) หรือคำสั่ง if / else

ตัวอย่างแบบอินไลน์ของฉันจะถูกเขียนใหม่ด้วยวิธีนี้:

Optional<Other> result =
    things.stream()
          .map(this::resolve)
          .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
          .findFirst();

ตัวอย่างของฉันที่ใช้เมธอดตัวช่วยจะถูกเขียนใหม่ด้วยวิธีนี้:

/**
 * Turns an Optional<T> into a Stream<T> of length zero or one depending upon
 * whether a value is present.
 */
static <T> Stream<T> streamopt(Optional<T> opt) {
    return opt.map(Stream::of)
              .orElseGet(Stream::empty);
}

Optional<Other> result =
    things.stream()
          .flatMap(t -> streamopt(resolve(t)))
          .findFirst();

COMMENTARY

ลองเปรียบเทียบเวอร์ชั่นเดิมกับเวอร์ชั่นที่แก้ไขโดยตรง:

// original
.flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())

// modified
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))

ต้นฉบับเป็นวิธีที่ตรงไปตรงมาหากคนงานมีวิธีการ: เราได้รับOptional<Other>; หากมีค่าเราจะส่งคืนสตรีมที่มีค่านั้นและหากไม่มีค่าเราจะส่งคืนสตรีมที่ว่างเปล่า ค่อนข้างง่ายและอธิบายได้ง่าย

การดัดแปลงนั้นฉลาดและมีข้อได้เปรียบที่หลีกเลี่ยงเงื่อนไข (ฉันรู้ว่าบางคนไม่ชอบผู้ประกอบการที่สามถ้าใช้ในทางที่ผิดมันอาจทำให้รหัสยากที่จะเข้าใจ) อย่างไรก็ตามบางครั้งสิ่งต่าง ๆ อาจฉลาดเกินไป Optional<Other>รหัสการแก้ไขนอกจากนี้ยังเริ่มต้นด้วยการ จากนั้นจะเรียกใช้Optional.mapซึ่งนิยามไว้ดังนี้:

หากมีค่าอยู่ให้ใช้ฟังก์ชันการทำแผนที่ที่มีให้กับมันและหากผลลัพธ์นั้นไม่ใช่ค่า Null ให้ส่งคืนทางเลือกที่อธิบายถึงผลลัพธ์ ไม่เช่นนั้นจะส่งคืนตัวเลือกเปล่า

ผลตอบแทนที่ได้การเรียกmap(Stream::of) Optional<Stream<Other>>หากมีค่าอยู่ในอินพุตเพิ่มเติมทางเลือกที่ส่งคืนจะมีสตรีมที่มีผลลัพธ์อื่น ๆ เพียงรายการเดียว แต่ถ้าไม่มีค่าผลลัพธ์จะเป็นตัวเลือกที่ว่างเปล่า

ถัดไปเรียกร้องให้ผลตอบแทนค่าของชนิดorElseGet(Stream::empty) Stream<Other>หากมีค่าอินพุตมันจะได้รับค่าซึ่งเป็นองค์ประกอบStream<Other>เดียว มิฉะนั้น (ถ้าค่าการป้อนข้อมูลที่ไม่อยู่) Stream<Other>ก็จะส่งกลับว่างเปล่า ดังนั้นผลลัพธ์จึงถูกต้องเหมือนกับรหัสที่มีเงื่อนไขดั้งเดิม

ในความคิดเห็นที่พูดคุยเกี่ยวกับคำตอบของฉันเกี่ยวกับการแก้ไขที่ถูกปฏิเสธฉันได้อธิบายเทคนิคนี้ว่า "กระชับขึ้น แต่ยังคลุมเครือมากกว่า" ฉันยืนตามนี้ ฉันใช้เวลาสักครู่เพื่อคิดออกว่ากำลังทำอะไรอยู่และใช้เวลาสักครู่ในการเขียนคำอธิบายข้างต้นเกี่ยวกับสิ่งที่ทำ ความละเอียดอ่อนที่สำคัญคือการเปลี่ยนแปลงจากการOptional<Other> Optional<Stream<Other>>เมื่อคุณคร่ำครวญเรื่องนี้มันก็สมเหตุสมผล แต่มันก็ไม่ชัดเจนสำหรับฉัน

ฉันจะยอมรับว่าสิ่งที่คลุมเครือในตอนแรกอาจกลายเป็นสำนวนตลอดเวลา อาจเป็นไปได้ว่าเทคนิคนี้จบลงด้วยการเป็นวิธีที่ดีที่สุดในการปฏิบัติอย่างน้อยก็จนกว่าจะOptional.streamได้รับการเพิ่ม (ถ้าเคย)

UPDATE: Optional.streamถูกเพิ่มใน JDK 9


16

คุณไม่สามารถทำให้รัดกุมมากขึ้นในขณะที่คุณกำลังทำอยู่

คุณอ้างว่าคุณไม่ต้องการและ.filter(Optional::isPresent) .map(Optional::get)

สิ่งนี้ได้รับการแก้ไขแล้วโดยวิธีการที่ @StartMarks อธิบายอย่างไรก็ตามในขณะนี้คุณได้แมปไปที่Optional<T>ดังนั้นตอนนี้คุณต้องใช้.flatMap(this::streamopt)และget()ในที่สุด

ดังนั้นมันจึงยังคงประกอบด้วยคำสั่งสองคำและคุณสามารถรับข้อยกเว้นด้วยวิธีการใหม่ได้! เพราะจะทำอย่างไรถ้าตัวเลือกทั้งหมดว่างเปล่า จากนั้นfindFirst()จะคืนค่าว่างเปล่าและคุณget()จะล้มเหลว!

ดังนั้นสิ่งที่คุณมี:

things.stream()
    .map(this::resolve)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();

เป็นจริงวิธีที่ดีที่สุดเพื่อให้บรรลุสิ่งที่คุณต้องการและที่คุณต้องการที่จะบันทึกผลเป็นไม่เป็นTOptional<T>

ฉันใช้เสรีภาพในการสร้างCustomOptional<T>คลาสที่ล้อมรอบOptional<T>และจัดเตรียมวิธีพิเศษ, flatStream(). โปรดทราบว่าคุณไม่สามารถขยายOptional<T>:

class CustomOptional<T> {
    private final Optional<T> optional;

    private CustomOptional() {
        this.optional = Optional.empty();
    }

    private CustomOptional(final T value) {
        this.optional = Optional.of(value);
    }

    private CustomOptional(final Optional<T> optional) {
        this.optional = optional;
    }

    public Optional<T> getOptional() {
        return optional;
    }

    public static <T> CustomOptional<T> empty() {
        return new CustomOptional<>();
    }

    public static <T> CustomOptional<T> of(final T value) {
        return new CustomOptional<>(value);
    }

    public static <T> CustomOptional<T> ofNullable(final T value) {
        return (value == null) ? empty() : of(value);
    }

    public T get() {
        return optional.get();
    }

    public boolean isPresent() {
        return optional.isPresent();
    }

    public void ifPresent(final Consumer<? super T> consumer) {
        optional.ifPresent(consumer);
    }

    public CustomOptional<T> filter(final Predicate<? super T> predicate) {
        return new CustomOptional<>(optional.filter(predicate));
    }

    public <U> CustomOptional<U> map(final Function<? super T, ? extends U> mapper) {
        return new CustomOptional<>(optional.map(mapper));
    }

    public <U> CustomOptional<U> flatMap(final Function<? super T, ? extends CustomOptional<U>> mapper) {
        return new CustomOptional<>(optional.flatMap(mapper.andThen(cu -> cu.getOptional())));
    }

    public T orElse(final T other) {
        return optional.orElse(other);
    }

    public T orElseGet(final Supplier<? extends T> other) {
        return optional.orElseGet(other);
    }

    public <X extends Throwable> T orElseThrow(final Supplier<? extends X> exceptionSuppier) throws X {
        return optional.orElseThrow(exceptionSuppier);
    }

    public Stream<T> flatStream() {
        if (!optional.isPresent()) {
            return Stream.empty();
        }
        return Stream.of(get());
    }

    public T getTOrNull() {
        if (!optional.isPresent()) {
            return null;
        }
        return get();
    }

    @Override
    public boolean equals(final Object obj) {
        return optional.equals(obj);
    }

    @Override
    public int hashCode() {
        return optional.hashCode();
    }

    @Override
    public String toString() {
        return optional.toString();
    }
}

คุณจะเห็นว่าฉันเพิ่มflatStream()ตามที่นี่:

public Stream<T> flatStream() {
    if (!optional.isPresent()) {
        return Stream.empty();
    }
    return Stream.of(get());
}

ใช้เป็น:

String result = Stream.of("a", "b", "c", "de", "fg", "hij")
        .map(this::resolve)
        .flatMap(CustomOptional::flatStream)
        .findFirst()
        .get();

คุณยังจะต้องกลับมาStream<T>ที่นี่ในขณะที่คุณไม่สามารถกลับTเพราะถ้า!optional.isPresent()แล้วT == nullถ้าคุณประกาศดังกล่าว แต่แล้วคุณ.flatMap(CustomOptional::flatStream)จะพยายามที่จะเพิ่มnullไปยังสตรีมและที่เป็นไปไม่ได้

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

public T getTOrNull() {
    if (!optional.isPresent()) {
        return null;
    }
    return get();
}

ใช้เป็น:

String result = Stream.of("a", "b", "c", "de", "fg", "hij")
        .map(this::resolve)
        .map(CustomOptional::getTOrNull)
        .findFirst()
        .get();

จะโยนNullPointerExceptionการดำเนินการภายในสตรีม

ข้อสรุป

วิธีที่คุณใช้จริง ๆ แล้วเป็นวิธีที่ดีที่สุด


6

รุ่นที่สั้นกว่าเล็กน้อยโดยใช้reduce:

things.stream()
  .map(this::resolve)
  .reduce(Optional.empty(), (a, b) -> a.isPresent() ? a : b );

นอกจากนี้คุณยังสามารถย้ายฟังก์ชั่นลดไปที่วิธียูทิลิตี้แบบคงที่และจากนั้นมันจะกลายเป็น:

  .reduce(Optional.empty(), Util::firstPresent );

6
ฉันชอบสิ่งนี้ แต่มันก็คุ้มค่าที่ชี้ให้เห็นว่าสิ่งนี้จะประเมินทุกรายการในสตรีมในขณะที่ findFirst () จะประเมินเฉพาะจนกว่าจะพบรายการปัจจุบัน
Duncan McGregor

1
และน่าเสียดายที่การดำเนินการแก้ปัญหาแต่ละครั้งเป็นดีลเบรกเกอร์ แต่มันก็ฉลาด
Yona Appletree

5

เนื่องจากคำตอบก่อนหน้าของฉันดูเหมือนจะไม่เป็นที่นิยมฉันจะให้อีก

คำตอบสั้น ๆ :

คุณอยู่ในเส้นทางที่ถูกต้องเป็นส่วนใหญ่ รหัสที่สั้นที่สุดเพื่อให้ได้ผลลัพธ์ที่คุณต้องการที่ฉันสามารถทำได้คือ:

things.stream()
      .map(this::resolve)
      .filter(Optional::isPresent)
      .findFirst()
      .flatMap( Function.identity() );

สิ่งนี้จะสอดคล้องกับความต้องการของคุณทั้งหมด:

  1. มันจะพบการตอบสนองแรกที่แก้ไขไปที่ไม่ว่างเปล่า Optional<Result>
  2. มันเรียกthis::resolveอย่างเกียจคร้านตามที่ต้องการ
  3. this::resolve จะไม่ถูกเรียกใช้หลังจากผลลัพธ์แรกที่ไม่ว่างเปล่า
  4. มันจะกลับมา Optional<Result>

คำตอบอีกต่อไป

การปรับเปลี่ยนเพียงอย่างเดียวเมื่อเทียบกับรุ่นเริ่มต้นของ OP คือฉันลบออก.map(Optional::get)ก่อนที่จะโทร.findFirst()และเพิ่ม.flatMap(o -> o)เป็นสายสุดท้ายในสายโซ่

สิ่งนี้มีผลดีในการกำจัดตัวเลือกสองทางเมื่อใดก็ตามที่สตรีมพบผลลัพธ์ที่แท้จริง

คุณไม่สามารถไปให้สั้นกว่านี้ใน Java ได้

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

  1. โทรthis.resolve,
  2. กรองตาม Optional.isPresent
  3. ส่งคืนผลลัพธ์และ
  4. วิธีจัดการกับผลลบ (เมื่อไม่พบสิ่งใด)

เพียงเพื่อพิสูจน์ว่าโซลูชันของฉันทำงานตามที่โฆษณาไว้ฉันได้เขียนโปรแกรมทดสอบขนาดเล็ก:

public class StackOverflow {

    public static void main( String... args ) {
        try {
            final int integer = Stream.of( args )
                    .peek( s -> System.out.println( "Looking at " + s ) )
                    .map( StackOverflow::resolve )
                    .filter( Optional::isPresent )
                    .findFirst()
                    .flatMap( o -> o )
                    .orElseThrow( NoSuchElementException::new )
                    .intValue();

            System.out.println( "First integer found is " + integer );
        }
        catch ( NoSuchElementException e ) {
            System.out.println( "No integers provided!" );
        }
    }

    private static Optional<Integer> resolve( String string ) {
        try {
            return Optional.of( Integer.valueOf( string ) );
        }
        catch ( NumberFormatException e )
        {
            System.out.println( '"' + string + '"' + " is not an integer");
            return Optional.empty();
        }
    }

}

(มันมีสายพิเศษไม่กี่บรรทัดสำหรับการแก้ไขข้อบกพร่องและตรวจสอบว่ามีเพียงการโทรจำนวนมากที่จะแก้ไขได้ตามต้องการ ... )

การดำเนินการนี้ในบรรทัดคำสั่งฉันได้ผลลัพธ์ดังต่อไปนี้:

$ java StackOferflow a b 3 c 4
Looking at a
"a" is not an integer
Looking at b
"b" is not an integer
Looking at 3
First integer found is 3

ฉันคิดเช่นเดียวกับ Roland Tepp ทำไมบางคนถึงทำให้สตรีม <สตรีม <? >> และแบนเมื่อคุณสามารถแบนด้วยตัวเลือกเดียว <ทางเลือก <? >>
Young Hyun Yoo

3

ถ้าคุณไม่คิดที่จะใช้ห้องสมุดของบุคคลที่สามที่คุณอาจจะใช้Javaslang มันเป็นเหมือน Scala แต่นำมาใช้ใน Java

มันมาพร้อมกับไลบรารีคอลเลกชันที่ไม่เปลี่ยนรูปแบบที่คล้ายคลึงกับ Scala คอลเล็กชันเหล่านี้แทนที่คอลเล็กชันของ Java และสตรีมของ Java 8 นอกจากนี้ยังมีการใช้งาน Option ของตัวเอง

import javaslang.collection.Stream;
import javaslang.control.Option;

Stream<Option<String>> options = Stream.of(Option.some("foo"), Option.none(), Option.some("bar"));

// = Stream("foo", "bar")
Stream<String> strings = options.flatMap(o -> o);

นี่คือวิธีแก้ปัญหาสำหรับตัวอย่างของคำถามเริ่มต้น:

import javaslang.collection.Stream;
import javaslang.control.Option;

public class Test {

    void run() {

        // = Stream(Thing(1), Thing(2), Thing(3))
        Stream<Thing> things = Stream.of(new Thing(1), new Thing(2), new Thing(3));

        // = Some(Other(2))
        Option<Other> others = things.flatMap(this::resolve).headOption();
    }

    Option<Other> resolve(Thing thing) {
        Other other = (thing.i % 2 == 0) ? new Other(i + "") : null;
        return Option.of(other);
    }

}

class Thing {
    final int i;
    Thing(int i) { this.i = i; }
    public String toString() { return "Thing(" + i + ")"; }
}

class Other {
    final String s;
    Other(String s) { this.s = s; }
    public String toString() { return "Other(" + s + ")"; }
}

คำเตือน: ฉันเป็นผู้สร้างของ Javaslang


3

สายไปงานเลี้ยง แต่สิ่งที่เกี่ยวกับ

things.stream()
    .map(this::resolve)
    .filter(Optional::isPresent)
    .findFirst().get();

คุณสามารถกำจัด get สุดท้าย () หากคุณสร้างเมธอด util เพื่อแปลงทางเลือกเพื่อสตรีมด้วยตนเอง:

things.stream()
    .map(this::resolve)
    .flatMap(Util::optionalToStream)
    .findFirst();

หากคุณกลับกระแสทันทีจากฟังก์ชั่นการแก้ไขของคุณคุณบันทึกอีกหนึ่งบรรทัด


3

ฉันต้องการส่งเสริมวิธีการของโรงงานในการสร้างผู้ช่วยเหลือสำหรับ API การทำงาน:

Optional<R> result = things.stream()
        .flatMap(streamopt(this::resolve))
        .findFirst();

วิธีการของโรงงาน:

<T, R> Function<T, Stream<R>> streamopt(Function<T, Optional<R>> f) {
    return f.andThen(Optional::stream); // or the J8 alternative:
    // return t -> f.apply(t).map(Stream::of).orElseGet(Stream::empty);
}

เหตุผล:

  • เช่นเดียวกับการอ้างอิงวิธีการทั่วไปเปรียบเทียบกับแลมบ์ดานิพจน์คุณไม่สามารถจับตัวแปรจากขอบเขตที่เข้าถึงได้โดยไม่ตั้งใจเช่น:

    t -> streamopt(resolve(o))

  • มันสามารถจัดเรียงได้เช่นคุณสามารถโทรหาFunction::andThenผลลัพธ์วิธีการของโรงงาน:

    streamopt(this::resolve).andThen(...)

    ในกรณีของแลมบ์ดาคุณต้องขว้างมันก่อน:

    ((Function<T, Stream<R>>) t -> streamopt(resolve(t))).andThen(...)


3

Null รับการสนับสนุนโดยกระแสที่ให้บริการห้องสมุดของฉันAbacusUtil นี่คือรหัส:

Stream.of(things).map(e -> resolve(e).orNull()).skipNull().first();

3

หากคุณติดกับ Java 8 แต่มีสิทธิ์เข้าถึง Guava 21.0 หรือใหม่กว่าคุณสามารถใช้Streams.streamในการแปลงตัวเลือกเป็นสตรีม

ดังนั้นได้รับ

import com.google.common.collect.Streams;

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

Optional<Other> result =
    things.stream()
        .map(this::resolve)
        .flatMap(Streams::stream)
        .findFirst();

0

แล้วเรื่องอะไรล่ะ

private static List<String> extractString(List<Optional<String>> list) {
    List<String> result = new ArrayList<>();
    list.forEach(element -> element.ifPresent(result::add));
    return result;
}

https://stackoverflow.com/a/58281000/3477539


ทำไมต้องทำเช่นนี้เมื่อคุณสามารถสตรีมและรวบรวมได้
OneCricketeer

return list.stream().filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()))เช่นเดียวกับคำถาม (และคำตอบที่เชื่อมโยงของคุณ) มี ...
OneCricketeer

ฉันอาจจะผิด แต่ฉันคิดว่าการใช้ isPresent () แล้ว get () ไม่ใช่วิธีปฏิบัติที่ดี ดังนั้นฉันจึงพยายามหลีกหนีจากสิ่งนั้น
rastaman

หากคุณ.get() ไม่ ใช้งานisPresent()คุณจะได้รับคำเตือนใน IntelliJ
OneCricketeer

-5

เป็นไปได้ว่าคุณทำผิด

Java 8 เผื่อเลือกไม่ได้มีไว้สำหรับใช้ในลักษณะนี้ โดยปกติจะสงวนไว้เฉพาะสำหรับการดำเนินการกระแสข้อมูลเทอร์มินัลที่อาจหรืออาจไม่ส่งคืนค่าเช่นค้นหาตัวอย่าง

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

things.filter(Thing::isResolvable)
      .findFirst()
      .flatMap(this::resolve)
      .get();

กฎของหัวแม่มือคือคุณควรพยายามลดจำนวนรายการในสตรีมก่อนที่คุณจะเปลี่ยนเป็นสิ่งอื่น แน่นอน YMMV


6
ฉันคิดว่าวิธีแก้ไขของ OP () ส่งคืนตัวเลือก <Other> คือการใช้ตัวเลือกอย่างสมบูรณ์แบบ แน่นอนว่าฉันไม่สามารถพูดกับโดเมนปัญหาของ OP ได้ แต่อาจเป็นไปได้ว่าวิธีการตรวจสอบว่ามีบางสิ่งที่แก้ไขได้คือพยายามแก้ไขมัน ถ้าเป็นเช่นนั้นตัวเลือกจะรวมผลลัพธ์บูลีนเป็น "แก้ไขได้นี้" กับผลลัพธ์ของการแก้ไขหากประสบความสำเร็จในการเรียก API เดียว
Stuart Marks

2
สจวร์ตนั้นถูกต้องแล้ว ฉันมีชุดคำค้นหาตามลำดับความต้องการและฉันต้องการค้นหาผลลัพธ์ของคำแรกที่ส่งคืนสิ่งใด Optional<Result> searchFor(Term t)ดังนั้นโดยทั่วไป ที่ดูเหมือนว่าจะพอดีกับความตั้งใจของตัวเลือก นอกจากนี้สตรีม () ยังควรได้รับการประเมินอย่างเกียจคร้านดังนั้นจึงไม่มีงานพิเศษที่จะแก้ไขคำศัพท์ที่ผ่านมาครั้งแรกที่ตรงกัน
Yona Appletree

คำถามนี้มีเหตุผลอย่างสมบูรณ์แบบและการใช้ flatMap กับทางเลือกนั้นมักจะถูกฝึกในภาษาอื่น ๆ ที่คล้ายกันเช่น Scala
DZS
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.