กรอง Java Stream เป็น 1 และ 1 องค์ประกอบเท่านั้น


230

ฉันกำลังพยายามที่จะใช้ Java 8 เพื่อหาองค์ประกอบในStream LinkedListฉันต้องการรับประกันว่ามีเพียงหนึ่งเดียวที่ตรงกับเกณฑ์ตัวกรอง

ใช้รหัสนี้:

public static void main(String[] args) {

    LinkedList<User> users = new LinkedList<>();
    users.add(new User(1, "User1"));
    users.add(new User(2, "User2"));
    users.add(new User(3, "User3"));

    User match = users.stream().filter((user) -> user.getId() == 1).findAny().get();
    System.out.println(match.toString());
}

static class User {

    @Override
    public String toString() {
        return id + " - " + username;
    }

    int id;
    String username;

    public User() {
    }

    public User(int id, String username) {
        this.id = id;
        this.username = username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public int getId() {
        return id;
    }
}

รหัสนี้ค้นหาUserตาม ID ของพวกเขา แต่ไม่มีการรับประกันว่าจะมีจำนวนเท่าใดUserที่ตรงกับตัวกรอง

การเปลี่ยนบรรทัดตัวกรองเป็น:

User match = users.stream().filter((user) -> user.getId() < 0).findAny().get();

จะโยนNoSuchElementException(ดี!)

ฉันต้องการให้เกิดข้อผิดพลาดหากมีการแข่งขันหลายรายการ มีวิธีทำเช่นนี้หรือไม่?


count()เป็นการทำงานของเครื่องปลายทางดังนั้นคุณจึงไม่สามารถทำได้ ไม่สามารถใช้สตรีมได้ในภายหลัง
Alexis C.

ตกลงขอบคุณ @ZouZou ฉันไม่แน่ใจทั้งหมดว่าวิธีการนั้นทำอะไร ทำไมถึงไม่มีStream::size?
ryvantage

7
@ryvantage เนื่องจากสตรีมสามารถใช้ได้เพียงครั้งเดียวเท่านั้น: การคำนวณขนาดของมันหมายถึง "วนซ้ำ" เหนือสตรีมและหลังจากนั้นคุณจะไม่สามารถใช้สตรีมได้อีกต่อไป
assylias

3
ว้าว. ความคิดเห็นเดียวนั้นช่วยให้ฉันเข้าใจStreamมากขึ้นกว่าที่ฉันเคยทำมาก่อน ...
ryvantage

2
นี่คือเมื่อคุณตระหนักว่าคุณจำเป็นต้องใช้LinkedHashSet(สมมติว่าคุณต้องการเก็บรักษาคำสั่งการแทรก) หรือHashSetทั้งหมด หากคอลเล็กชันของคุณใช้เพื่อค้นหา ID ผู้ใช้เดียวดังนั้นทำไมคุณจึงรวบรวมรายการอื่นทั้งหมด หากมีความเป็นไปได้ที่คุณจะต้องค้นหา ID ผู้ใช้ที่จำเป็นต้องมีเอกลักษณ์อยู่เสมอทำไมจึงใช้รายการและไม่ได้ตั้งค่า คุณกำลังเขียนโปรแกรมย้อนหลัง ใช้คอลเล็กชันที่เหมาะสมสำหรับงานและช่วยตัวเองด้วยอาการปวดหัวนี้
smac89

คำตอบ:


192

สร้างที่กำหนดเอง Collector

public static <T> Collector<T, ?, T> toSingleton() {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> {
                if (list.size() != 1) {
                    throw new IllegalStateException();
                }
                return list.get(0);
            }
    );
}

เราใช้Collectors.collectingAndThenเพื่อสร้างสิ่งที่เราต้องการCollectorโดย

  1. รวบรวมวัตถุของเราในListกับCollectors.toList()นักสะสม
  2. การใช้หมัดเด็ดเสริมในตอนท้ายผลตอบแทนว่าองค์ประกอบเดียว - หรือพ่นถ้าIllegalStateExceptionlist.size != 1

ใช้เป็น:

User resultUser = users.stream()
        .filter(user -> user.getId() > 0)
        .collect(toSingleton());

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

ทางเลือก - โซลูชันที่สวยงามน้อยกว่า:

คุณสามารถใช้ 'การแก้ปัญหา' ที่เกี่ยวข้องpeek()และได้AtomicIntegerแต่จริงๆแล้วคุณไม่ควรใช้สิ่งนั้น

สิ่งที่คุณสามารถทำได้คือแค่รวบรวมมันในแบบListนี้:

LinkedList<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));
List<User> resultUserList = users.stream()
        .filter(user -> user.getId() == 1)
        .collect(Collectors.toList());
if (resultUserList.size() != 1) {
    throw new IllegalStateException();
}
User resultUser = resultUserList.get(0);

24
Guava's Iterables.getOnlyElementจะทำให้โซลูชั่นเหล่านี้สั้นลงและให้ข้อความแสดงข้อผิดพลาดที่ดีขึ้น เป็นเคล็ดลับสำหรับผู้อ่านเพื่อนที่ใช้ Google Guava อยู่แล้ว
ทิมBüthe

2
ฉันห่อความคิดนี้ขึ้นเป็นชั้น - gist.github.com/denov/a7eac36a3cda041f8afeabcef09d16fc
denov

1
@LonelyNeuron โปรดอย่าแก้ไขรหัสของฉัน มันทำให้ฉันอยู่ในสถานการณ์ที่ฉันต้องตรวจสอบคำตอบทั้งหมดของฉันซึ่งฉันได้เขียนสี่ปีที่ผ่านมาและฉันก็ไม่มีเวลาสำหรับมันตอนนี้
skiwi

2
@skiwi: การแก้ไขของ Lonely มีประโยชน์และถูกต้องดังนั้นฉันจึงทำการติดตั้งใหม่หลังจากการตรวจสอบ ผู้ที่เข้ามาที่คำตอบนี้วันนี้ไม่สนใจว่าคุณมาที่คำตอบอย่างไรพวกเขาไม่จำเป็นต้องดูเวอร์ชันเก่าและเวอร์ชั่นใหม่และส่วนที่อัปเดต ทำให้คำตอบของคุณสับสนมากขึ้นและมีประโยชน์น้อยลง เป็นการดีกว่ามากหากวางโพสต์ในสถานะสุดท้ายและหากผู้คนต้องการดูว่าโพสต์นั้นเล่นอย่างไรพวกเขาสามารถดูประวัติโพสต์ได้
Martijn Pieters

1
@skiwi: รหัสในคำตอบคือแน่นอนสิ่งที่คุณเขียน ทั้งหมดแก้ไขไม่ได้สะอาดขึ้นโพสต์ของคุณเพียงลบเวอร์ชันก่อนหน้าของคำนิยามจำหน่ายแล้วโดยรุ่นที่ยังคงอยู่ในโพสต์และเปลี่ยนชื่อใหม่เพื่อsingletonCollector() toSingleton()ความเชี่ยวชาญในการสตรีม Java ของฉันค่อนข้างสนิม แต่การเปลี่ยนชื่อดูมีประโยชน์สำหรับฉัน การตรวจสอบการเปลี่ยนแปลงนี้ใช้เวลา 2 นาทีกับฉัน หากคุณไม่ได้มีเวลาที่จะตรวจสอบการแก้ไขผมสามารถแสดงให้เห็นว่าคุณถามคนอื่นที่จะทำเช่นนี้ในอนาคตอาจจะเป็นในห้องแชท Java ?
Martijn Pieters

118

เพื่อความสมบูรณ์นี่คือ 'หนึ่งซับ' ที่สอดคล้องกับคำตอบที่ยอดเยี่ยมของ @ prunge:

User user1 = users.stream()
        .filter(user -> user.getId() == 1)
        .reduce((a, b) -> {
            throw new IllegalStateException("Multiple elements: " + a + ", " + b);
        })
        .get();

สิ่งนี้ได้รับองค์ประกอบการจับคู่ แต่เพียงผู้เดียวจากสตรีมการขว้าง

  • NoSuchElementException ในกรณีที่กระแสข้อมูลว่างเปล่าหรือ
  • IllegalStateException ในกรณีที่กระแสมีมากกว่าหนึ่งองค์ประกอบที่ตรงกัน

การเปลี่ยนแปลงของวิธีการนี้หลีกเลี่ยงการโยนข้อยกเว้น แต่เนิ่นๆและแทนผลลัพธ์ที่เป็นOptionalองค์ประกอบที่มีทั้งองค์ประกอบเดียวหรือไม่มีอะไร (ว่างเปล่า) หากมีองค์ประกอบที่เป็นศูนย์หรือหลายรายการ:

Optional<User> user1 = users.stream()
        .filter(user -> user.getId() == 1)
        .collect(Collectors.reducing((a, b) -> null));

3
ฉันชอบวิธีการเริ่มต้นในคำตอบนี้ เพื่อจุดประสงค์ในการปรับแต่งเองคุณสามารถแปลงสุดท้ายget()เป็นorElseThrow()
arin

1
ฉันชอบความกระชับของสิ่งนี้และความจริงที่ว่ามันหลีกเลี่ยงการสร้างรายการที่ไม่จำเป็นในแต่ละครั้งที่เรียก
LordOfThePigs

83

คำตอบอื่น ๆ ที่เกี่ยวข้องกับการเขียนแบบกำหนดเองCollectorน่าจะมีประสิทธิภาพมากกว่า (เช่นLouis Wasserman's , +1) แต่ถ้าคุณต้องการความกระชับฉันขอแนะนำสิ่งต่อไปนี้:

List<User> result = users.stream()
    .filter(user -> user.getId() == 1)
    .limit(2)
    .collect(Collectors.toList());

จากนั้นตรวจสอบขนาดของรายการผลลัพธ์

if (result.size() != 1) {
  throw new IllegalStateException("Expected exactly one user but got " + result);
User user = result.get(0);
}

5
ประเด็นlimit(2)ในการแก้ปัญหานี้คืออะไร? มันจะแตกต่างกันอย่างไรถ้ารายการผลลัพธ์เป็น 2 หรือ 100 ถ้ามันมากกว่า 1
ryvantage

18
มันจะหยุดทันทีหากพบคู่ที่สอง นี่คือสิ่งที่นักสะสมแฟนซีทำเพียงแค่ใช้รหัสมากขึ้น :-)
Stuart Marks

10
วิธีการเกี่ยวกับการเพิ่มCollectors.collectingAndThen(toList(), l -> { if (l.size() == 1) return l.get(0); throw new RuntimeException(); })
Lukas Eder

1
Javadoc กล่าวสิ่งนี้เกี่ยวกับพารามิเตอร์ของขีด จำกัด : maxSize: the number of elements the stream should be limited to. ดังนั้นมันไม่ควรจะเป็น.limit(1)แทนที่จะ.limit(2)?
alexbt

5
@alexbt คำแถลงปัญหาคือเพื่อให้แน่ใจว่ามีองค์ประกอบการจับคู่หนึ่ง (ไม่มากไม่น้อยกว่า) หลังจากรหัสของฉันหนึ่งสามารถทดสอบresult.size()เพื่อให้แน่ใจว่ามันเท่ากับ 1 ถ้าเป็น 2 แล้วมีมากกว่าหนึ่งการแข่งขันจึงเป็นข้อผิดพลาด หากรหัสทำlimit(1)เช่นนั้นการแข่งขันมากกว่าหนึ่งรายการจะส่งผลให้มีองค์ประกอบเดียวซึ่งไม่สามารถแยกความแตกต่างจากการมีการจับคู่หนึ่งรายการ นี่จะพลาดกรณีข้อผิดพลาดที่ OP เกี่ยวข้อง
Stuart Marks

67

ฝรั่งให้MoreCollectors.onlyElement()ซึ่งสิ่งที่ถูกต้องที่นี่ แต่ถ้าคุณต้องทำด้วยตัวเองคุณสามารถทำสิ่งนี้ได้เองCollector:

<E> Collector<E, ?, Optional<E>> getOnly() {
  return Collector.of(
    AtomicReference::new,
    (ref, e) -> {
      if (!ref.compareAndSet(null, e)) {
         throw new IllegalArgumentException("Multiple values");
      }
    },
    (ref1, ref2) -> {
      if (ref1.get() == null) {
        return ref2;
      } else if (ref2.get() != null) {
        throw new IllegalArgumentException("Multiple values");
      } else {
        return ref1;
      }
    },
    ref -> Optional.ofNullable(ref.get()),
    Collector.Characteristics.UNORDERED);
}

... หรือใช้ของคุณเองชนิดแทนHolder AtomicReferenceคุณสามารถใช้ซ้ำได้Collectorมากเท่าที่คุณต้องการ


@ skiwi singletonCollector นั้นเล็กลงและง่ายต่อการติดตามมากกว่านี้นั่นคือเหตุผลที่ฉันให้เช็ค แต่ดีที่จะเห็นฉันทามติในคำตอบ: ประเพณีCollectorเป็นวิธีที่จะไป
ryvantage

1
ยุติธรรมพอสมควร ฉันมุ่งเป้าไปที่ความเร็วไม่ใช่ความรัดกุม
Louis Wasserman

1
ใช่? ทำไมคุณถึงเร็วกว่า
ryvantage

3
ส่วนใหญ่เป็นเพราะการจัดสรร all-up Listมีราคาแพงกว่าการอ้างอิงที่ไม่แน่นอนเดียว
Louis Wasserman

1
@LouisWasserman ประโยคสุดท้ายที่MoreCollectors.onlyElement()ควรอัปเดตควรเป็นอันดับแรก (และอาจเป็นเพียง :))
Piotr Findeisen

46

ใช้ Guava's MoreCollectors.onlyElement()( JavaDoc )

มันทำในสิ่งที่คุณต้องการและโยนIllegalArgumentExceptionถ้ากระแสประกอบด้วยสององค์ประกอบหรือมากกว่าและNoSuchElementExceptionถ้ากระแสนั้นว่างเปล่า

การใช้งาน:

import static com.google.common.collect.MoreCollectors.onlyElement;

User match =
    users.stream().filter((user) -> user.getId() < 0).collect(onlyElement());

2
หมายเหตุสำหรับผู้ใช้รายอื่น: MoreCollectorsเป็นส่วนหนึ่งของรุ่นที่ยังไม่ได้เผยแพร่ (ณ วันที่ 2016-12) เวอร์ชันที่ยังไม่ได้เผยแพร่ 21.
qerub

2
คำตอบนี้ควรจะไปด้านบน
Emdadul Sawon

31

การดำเนินการ "escape hatch" ที่ให้คุณทำสิ่งแปลก ๆ ที่ไม่ได้รับการสนับสนุนจากสตรีมคือการขอIterator:

Iterator<T> it = users.stream().filter((user) -> user.getId() < 0).iterator();
if (!it.hasNext()) 
    throw new NoSuchElementException();
else {
    result = it.next();
    if (it.hasNext())
        throw new TooManyElementsException();
}

Guava มีวิธีอำนวยความสะดวกในการIteratorรับและรับเฉพาะองค์ประกอบโดยการขว้างปาหากมีองค์ประกอบเป็นศูนย์หรือหลายอย่างซึ่งสามารถแทนที่บรรทัด n-1 ด้านล่างที่นี่


4
วิธีการของ Guava: Iterators.getOnlyElement (Iterator <T> iterator)
anre

23

ปรับปรุง

ข้อเสนอแนะที่ดีในความคิดเห็นจาก @Holger:

Optional<User> match = users.stream()
              .filter((user) -> user.getId() > 1)
              .reduce((u, v) -> { throw new IllegalStateException("More than one ID found") });

คำตอบเดิม

มีข้อยกเว้นเกิดขึ้นOptional#getแต่ถ้าคุณมีมากกว่าหนึ่งองค์ประกอบที่จะไม่ช่วย คุณสามารถรวบรวมผู้ใช้ในคอลเล็กชันที่ยอมรับเพียงหนึ่งรายการเท่านั้นตัวอย่างเช่น:

User match = users.stream().filter((user) -> user.getId() > 1)
                  .collect(toCollection(() -> new ArrayBlockingQueue<User>(1)))
                  .poll();

ซึ่งพ่น a java.lang.IllegalStateException: Queue fullแต่รู้สึกแฮ็คเกินไป

หรือคุณสามารถใช้การลดร่วมกับตัวเลือก:

User match = Optional.ofNullable(users.stream().filter((user) -> user.getId() > 1)
                .reduce(null, (u, v) -> {
                    if (u != null && v != null)
                        throw new IllegalStateException("More than one ID found");
                    else return u == null ? v : u;
                })).get();

การลดลงจะส่งกลับ:

  • null หากไม่พบผู้ใช้
  • ผู้ใช้หากพบเพียงหนึ่งเดียว
  • โยนข้อยกเว้นหากพบมากกว่าหนึ่ง

ผลลัพธ์จะถูกห่อเป็นตัวเลือก

แต่วิธีแก้ปัญหาที่ง่ายที่สุดอาจเป็นเพียงแค่รวบรวมไปยังคอลเลกชันตรวจสอบว่าขนาดของมันคือ 1 และรับองค์ประกอบเดียว


1
ฉันจะเพิ่มเอกลักษณ์องค์ประกอบ ( null) get()เพื่อป้องกันการใช้ น่าเศร้าที่คุณreduceไม่ได้ทำงานอย่างที่คุณคิดลองพิจารณาองค์ประกอบที่Streamมีnullอยู่ในนั้นบางทีคุณคิดว่าคุณปิดมันไว้ แต่ฉันทำได้[User#1, null, User#2, null, User#3]ตอนนี้มันจะไม่ส่งข้อยกเว้นที่ฉันคิดยกเว้นว่าฉันเข้าใจผิดที่นี่
skiwi

2
@Skiwi หากมีองค์ประกอบที่ไม่ถูกต้องตัวกรองจะส่ง NPE ไปก่อน
assylias

2
เมื่อคุณรู้ว่ากระแสไม่สามารถผ่านnullฟังก์ชั่นการลดการลบอาร์กิวเมนต์ค่าเอกลักษณ์จะทำให้การซื้อขายทั้งหมดที่มีnullในการทำงานล้าสมัย: reduce( (u,v) -> { throw new IllegalStateException("More than one ID found"); } )ไม่ทำงานและดียิ่งขึ้นแล้วมันกลับOptional, eliding จำเป็นสำหรับการโทรOptional.ofNullableบน ผลลัพธ์.
Holger

15

อีกทางเลือกหนึ่งคือการใช้การลด: (ตัวอย่างนี้ใช้สตริง แต่สามารถนำไปใช้กับวัตถุชนิดใดก็ได้รวมถึงUser)

List<String> list = ImmutableList.of("one", "two", "three", "four", "five", "two");
String match = list.stream().filter("two"::equals).reduce(thereCanBeOnlyOne()).get();
//throws NoSuchElementException if there are no matching elements - "zero"
//throws RuntimeException if duplicates are found - "two"
//otherwise returns the match - "one"
...

//Reduction operator that throws RuntimeException if there are duplicates
private static <T> BinaryOperator<T> thereCanBeOnlyOne()
{
    return (a, b) -> {throw new RuntimeException("Duplicate elements found: " + a + " and " + b);};
}

ดังนั้นสำหรับกรณีที่Userคุณจะมี:

User match = users.stream().filter((user) -> user.getId() < 0).reduce(thereCanBeOnlyOne()).get();

8

ใช้ลด

นี่เป็นวิธีที่ง่ายและยืดหยุ่นที่ฉันพบ (ตามคำตอบ @prunge)

Optional<User> user = users.stream()
        .filter(user -> user.getId() == 1)
        .reduce((a, b) -> {
            throw new IllegalStateException("Multiple elements: " + a + ", " + b);
        })

วิธีนี้คุณจะได้รับ:

  • ตัวเลือก - เช่นเคยกับวัตถุของคุณหรือOptional.empty()ถ้าไม่มี
  • ข้อยกเว้น (กับประเภท / ข้อความที่กำหนดเองของคุณ) หากมีองค์ประกอบมากกว่าหนึ่งองค์ประกอบ

6

ฉันคิดว่าวิธีนี้ง่ายกว่า:

User resultUser = users.stream()
    .filter(user -> user.getId() > 0)
    .findFirst().get();

4
พบเพียงครั้งแรก แต่กรณีนี้ก็คือการโยนข้อยกเว้นเมื่อมันมีมากกว่าหนึ่ง
lczapski

5

ใช้Collector:

public static <T> Collector<T, ?, Optional<T>> toSingleton() {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> list.size() == 1 ? Optional.of(list.get(0)) : Optional.empty()
    );
}

การใช้งาน:

Optional<User> result = users.stream()
        .filter((user) -> user.getId() < 0)
        .collect(toSingleton());

เราส่งคืนOptionalเนื่องจากเรามักจะไม่สามารถสันนิษฐานได้ว่าCollectionจะมีองค์ประกอบหนึ่งอย่างแน่นอน หากคุณรู้อยู่แล้วว่าเป็นกรณีนี้ให้โทร:

User user = result.orElseThrow();

สิ่งนี้ทำให้ภาระในการจัดการกับข้อผิดพลาดของผู้โทร - เท่าที่ควร



1

เราสามารถใช้RxJava ( ไลบรารีส่วนขยายรีแอคทีฟที่มีประสิทธิภาพมาก)

LinkedList<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));

User userFound =  Observable.from(users)
                  .filter((user) -> user.getId() == 1)
                  .single().toBlocking().first();

เดียว ประกอบการพ่นยกเว้นถ้าไม่มีผู้ใช้หรือมากขึ้นแล้วผู้ใช้คนหนึ่งพบว่า


คำตอบที่ถูกต้องการเริ่มต้นสตรีมการบล็อกหรือการรวบรวมอาจไม่ถูกมากนัก (ในแง่ของทรัพยากร)
Karl Richter

1

ในฐานะที่Collectors.toMap(keyMapper, valueMapper)เป็นการใช้การควบรวมกิจการขว้างปาเพื่อจัดการหลายรายการด้วยคีย์เดียวกันมันง่าย:

List<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));

int id = 1;
User match = Optional.ofNullable(users.stream()
  .filter(user -> user.getId() == id)
  .collect(Collectors.toMap(User::getId, Function.identity()))
  .get(id)).get();

คุณจะได้รับรหัสที่IllegalStateExceptionซ้ำกัน ifแต่ท้ายที่สุดผมไม่แน่ใจว่าถ้ารหัสจะไม่แม้กระทั่งอ่านได้มากขึ้นโดยใช้


1
ทางออกที่ดี! และถ้าคุณทำ.collect(Collectors.toMap(user -> "", Function.identity())).get("")คุณมีพฤติกรรมทั่วไปมากขึ้น
glglgl

1

ฉันใช้นักสะสมสองคนนี้:

public static <T> Collector<T, ?, Optional<T>> zeroOrOne() {
    return Collectors.reducing((a, b) -> {
        throw new IllegalStateException("More than one value was returned");
    });
}

public static <T> Collector<T, ?, T> onlyOne() {
    return Collectors.collectingAndThen(zeroOrOne(), Optional::get);
}

เรียบร้อย! onlyOne()พ่นIllegalStateExceptionสำหรับองค์ประกอบ> 1 และ NoSuchElementException` (ในOptional::get) สำหรับ 0 องค์ประกอบ
simon04

@ simon04 คุณสามารถเกินวิธีการที่จะใช้ของSupplier (Runtime)Exception
ซาเวียร์ดอรี

1

หากคุณไม่รังเกียจการใช้ห้องสมุดบุคคลที่สามSequenceMจากไซคลอปส์สตรีม (และLazyFutureStreamจากการตอบสนองแบบง่าย ) ทั้งคู่มีตัวดำเนินการเดี่ยวและตัวเลือกเดียว

singleOptional()โยนข้อยกเว้นหากมี0มากกว่าหรือ1องค์ประกอบในStreamมิฉะนั้นจะส่งกลับค่าเดียว

String result = SequenceM.of("x")
                          .single();

SequenceM.of().single(); // NoSuchElementException

SequenceM.of(1, 2, 3).single(); // NoSuchElementException

String result = LazyFutureStream.fromStream(Stream.of("x"))
                          .single();

singleOptional()ผลตอบแทนถ้ามีค่าหรือไม่มีค่ามากกว่าหนึ่งในOptional.empty()Stream

Optional<String> result = SequenceM.fromStream(Stream.of("x"))
                          .singleOptional(); 
//Optional["x"]

Optional<String> result = SequenceM.of().singleOptional(); 
// Optional.empty

Optional<String> result =  SequenceM.of(1, 2, 3).singleOptional(); 
// Optional.empty

การเปิดเผยข้อมูล - ฉันเป็นผู้เขียนของทั้งสองห้องสมุด


0

ฉันไปกับแนวทางโดยตรงและเพิ่งนำสิ่งไปใช้:

public class CollectSingle<T> implements Collector<T, T, T>, BiConsumer<T, T>, Function<T, T>, Supplier<T> {
T value;

@Override
public Supplier<T> supplier() {
    return this;
}

@Override
public BiConsumer<T, T> accumulator() {
    return this;
}

@Override
public BinaryOperator<T> combiner() {
    return null;
}

@Override
public Function<T, T> finisher() {
    return this;
}

@Override
public Set<Characteristics> characteristics() {
    return Collections.emptySet();
}

@Override //accumulator
public void accept(T ignore, T nvalue) {
    if (value != null) {
        throw new UnsupportedOperationException("Collect single only supports single element, "
                + value + " and " + nvalue + " found.");
    }
    value = nvalue;
}

@Override //supplier
public T get() {
    value = null; //reset for reuse
    return value;
}

@Override //finisher
public T apply(T t) {
    return value;
}


} 

ด้วยการทดสอบ JUnit:

public class CollectSingleTest {

@Test
public void collectOne( ) {
    List<Integer> lst = new ArrayList<>();
    lst.add(7);
    Integer o = lst.stream().collect( new CollectSingle<>());
    System.out.println(o);
}

@Test(expected = UnsupportedOperationException.class)
public void failOnTwo( ) {
    List<Integer> lst = new ArrayList<>();
    lst.add(7);
    lst.add(8);
    Integer o = lst.stream().collect( new CollectSingle<>());
}

}

การใช้งานนี้ไม่ได้เป็น threadsafe


0
User match = users.stream().filter((user) -> user.getId()== 1).findAny().orElseThrow(()-> new IllegalArgumentException());

5
แม้ว่ารหัสนี้อาจแก้ปัญหาได้รวมถึงคำอธิบายว่าทำไมและวิธีแก้ปัญหานี้จะช่วยปรับปรุงคุณภาพการโพสต์ของคุณได้อย่างไรและอาจส่งผลให้คะแนนมากขึ้น จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตไม่ใช่เพียงแค่คนที่ถามตอนนี้ โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบายและระบุข้อ จำกัด และสมมติฐานที่ใช้
เดวิดบั๊ก

-2

คุณได้ลองสิ่งนี้แล้ว

long c = users.stream().filter((user) -> user.getId() == 1).count();
if(c > 1){
    throw new IllegalStateException();
}

long count()
Returns the count of elements in this stream. This is a special case of a reduction and is equivalent to:

     return mapToLong(e -> 1L).sum();

This is a terminal operation.

ที่มา: https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html


3
มันบอกว่าcount()ไม่ดีที่จะใช้เพราะมันคือการดำเนินการสถานี
ryvantage

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