ลบองค์ประกอบแรก (ที่มีดัชนีศูนย์) จากกระแสตามเงื่อนไข


10

ฉันมีรหัสต่อไปนี้:

Stream<String> lines = reader.lines();

หากสตริงกำปั้นเท่ากับ"email"ฉันต้องการลบสตริงแรกจากกระแส สำหรับสตริงอื่น ๆ จากสตรีมฉันไม่ต้องการการตรวจสอบนี้

ฉันจะปวดร้าวได้อย่างไร

PS

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


3
และหากองค์ประกอบที่สองคือ "อีเมล" คุณไม่ต้องการวางหรือไม่
michalk

@ Michael คุณถูกต้อง
gstackoverflow

อืม ... มีสิ่งskip(long n)ที่ข้ามnองค์ประกอบแรกแต่ฉันไม่ทราบว่าคุณสามารถแก้ไขได้อย่างใด ...
5254

4
@ gstackoverflow หากไม่น่าเป็นไปได้ที่สองสตริงแรกในสตรีมที่เท่ากัน "อีเมล" สิ่งที่ฉันคิดว่าเป็นกรณีที่คุณกำลังพูดถึงส่วนหัวของไฟล์ csv คุณสามารถใช้stream.dropWhile(s -> s.equals("email"));
Eritrean

1
@Eritrean มันเพิ่งโพสต์;)
YCF_L

คำตอบ:


6

ในขณะที่ผู้อ่านจะอยู่ในสถานะที่ไม่ระบุหลังจากที่คุณสร้างกระแสของเส้นจากมันมันอยู่ในสถานะที่กำหนดไว้อย่างดีก่อนที่คุณจะทำมัน

ดังนั้นคุณสามารถทำได้

String firstLine = reader.readLine();
Stream<String> lines = reader.lines();
if(firstLine != null && !"email".equals(firstLine))
    lines = Stream.concat(Stream.of(firstLine), lines);

ซึ่งเป็นทางออกที่สะอาดที่สุดในความคิดของฉัน โปรดทราบว่านี่ไม่เหมือนกับของ Java 9 dropWhileซึ่งจะลดลงมากกว่าหนึ่งบรรทัดหากตรงกัน


เห็นด้วย - วิธีนี้ดีกว่า แต่ DropWhile - เป็นที่สอง น่าเสียดายที่ผู้เขียนตัดสินใจที่จะไม่โพสต์ความคิดนี้เป็นคำตอบที่แยกจากกัน
gstackoverflow

3

หากคุณไม่มีรายการและต้องทำด้วย a เท่านั้นStreamคุณสามารถทำได้ด้วยตัวแปร

สิ่งนี้คือคุณสามารถใช้ตัวแปรได้ถ้ามันเป็น "final" หรือ "final final" ดังนั้นคุณไม่สามารถใช้บูลีนตัวอักษรได้ คุณยังสามารถทำได้ด้วยAtomicBoolean:

Stream<String> stream  = Arrays.asList("test", "email", "foo").stream();

AtomicBoolean first = new AtomicBoolean(true);
stream.filter(s -> {
    if (first.compareAndSet(true, false)) {
        return !s.equals("email");
    }
    return true;
})
// Then here, do whatever you need
.forEach(System.out::println);

หมายเหตุ: ฉันไม่ชอบการใช้ "ตัวแปรภายนอก" ในStreamเพราะผลข้างเคียงเป็นแนวปฏิบัติที่ไม่ดีในกระบวนทัศน์การเขียนโปรแกรมใช้งาน ยินดีต้อนรับตัวเลือกที่ดีกว่า


การใช้compareAndSetเป็นวิธีที่ยอดเยี่ยมในการตั้งค่าและรับค่าสถานะในเวลาเดียวกัน
f1sh

สามารถทำได้stream.filter(!first.compareAndSet(true, false) || !s.equals("email"))แม้ว่าจะเป็นที่ถกเถียงกัน
Joop Eggen

1
predicateต้องไร้สัญชาติ
ZhekaKozlov

3
หากการใช้AtomicBooleanที่นี่คือการรองรับกระแสข้อมูลแบบขนาน (ตามการใช้งานของการcompareAndSetแนะนำ) นี่เป็นความผิดอย่างสมบูรณ์เพราะจะใช้งานได้เฉพาะในกรณีที่กระแสเป็นลำดับ หากคุณใช้เทคนิคด้วยstream.sequential().filter(...)อย่างไรก็ตามฉันเห็นด้วยว่ามันจะใช้งานได้
daniel

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

1

เพื่อหลีกเลี่ยงการตรวจสอบเงื่อนไขในแต่ละบรรทัดของไฟล์ฉันเพียงแค่อ่านและตรวจสอบบรรทัดแรกแยกจากนั้นเรียกใช้ไพพ์ไลน์ที่เหลือของบรรทัดโดยไม่ตรวจสอบเงื่อนไข:

String first = reader.readLine();
Stream<String> firstLines = Optional.of(first)
        .filter(s -> !"email".equals(s))
        .map(s -> Stream.of(s))
        .orElseGet(() -> Stream.empty());

Stream<String> lines = Stream.concat(firstLines, reader.lines());

เรียบง่ายบน Java 9+:

Stream<String> firstLines = Optional.of(first)
        .filter(s -> !"email".equals(s))
        .stream();

Stream<String> lines = Stream.concat(firstLines, reader.lines());

4
Java 9 นั้นเรียบง่ายกว่าเดิม:Stream.ofNullable(first).filter(not("email"::equals))
Holger

1

@Arnouds คำตอบถูกต้อง คุณสามารถสร้างหนึ่งสตรีมสำหรับบรรทัดแรกแล้วเปรียบเทียบดังนี้

Stream<String> firstLineStream = reader.lines().limit(1).filter(line -> !line.startsWith("email"));;
Stream<String> remainingLinesStream = reader.lines().skip(1);
Stream.concat(firstLineStream, remainingLinesStream);

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

นอกจากจะไม่ทำงานให้กับผู้อ่านแล้วlimit(0)ก็ควรที่จะlimit(1)...
โฮลเกอร์

ฉันได้แก้ไขคำตอบเพื่อ จำกัด 0 หลังจาก testimg
Code_Mode

1
ฉันเห็นว่าคุณเปลี่ยนมัน แต่.limit(0).filter(line -> !line.startsWith("email"))ไม่มีเหตุผล ภาคแสดงจะไม่ได้รับการประเมิน สำหรับแหล่งข้อมูลสตรีมที่สามารถสร้างสตรีมได้การรวมกันของlimit(1)ในครั้งแรกและครั้งskip(1)ที่สองจะถูกต้อง สำหรับแหล่งที่มาของกระแสเหมือนอ่านค่าlimit(1)มิได้limit(0)ทำงานจะ คุณเพิ่งเปลี่ยนบรรทัดที่ถูกกลืนโดยไม่มีเงื่อนไข แม้ว่าคุณจะพบชุดค่าผสมที่เกิดขึ้นเพื่อทำสิ่งที่ต้องการ แต่มันก็ยังอยู่บนพื้นฐานของพฤติกรรมที่ขึ้นกับการใช้งานที่ไม่ระบุรายละเอียด
Holger

0

หากต้องการกรององค์ประกอบตามดัชนีคุณสามารถใช้AtomicIntegerเพื่อจัดเก็บและเพิ่มดัชนีขณะประมวลผล a Stream:

private static void filter(Stream<String> stream) {
  AtomicInteger index = new AtomicInteger();
  List<String> result = stream
      .filter(el -> {
        int i = index.getAndIncrement();
        return i > 0 || (i == 0 && !"email".equals(el));
      })
      .collect(toList());
  System.out.println(result);
}

public static void main(String[] args) {
  filter(Stream.of("email", "test1", "test2", "test3")); 
  //[test1, test2, test3]

  filter(Stream.of("test1", "email", "test2", "test3")); 
  //[test1, email, test2, test3]

  filter(Stream.of("test1", "test2", "test3")); 
  //[test1, test2, test3]
}

วิธีการนี้อนุญาตให้กรององค์ประกอบที่ดัชนีใด ๆ ไม่เพียง แต่องค์ประกอบแรก


0

ซับซ้อนกว่านี้เล็กน้อยรับแรงบันดาลใจบางอย่างจากตัวอย่างนี้

คุณสามารถสร้างStream<Integer>ที่จะแสดงดัชนีและ "zip" ด้วยของคุณStream<String>เพื่อสร้างStream<Pair<String, Integer>>

จากนั้นกรองโดยใช้ดัชนีและแมปกลับไปที่ Stream<String>

public static void main(String[] args) {
    Stream<String> s = reader.lines();
    Stream<Integer> indexes = Stream.iterate(0, i -> i + 1);

    zip(s, indexes)
        .filter(pair -> !(pair.getKey().equals("email") && pair.getValue() == 0))
        .map(Pair::getKey)
        .forEach(System.out::println);
}

private static Stream<Pair<String,Integer>> zip(Stream<String> stringStream, Stream<Integer> indexesStream){
    Iterable<Pair<String,Integer>> iterable = () -> new ZippedWithIndexIterator(stringStream.iterator(), indexesStream.iterator());
    return StreamSupport.stream(iterable.spliterator(), false);
}

static class ZippedWithIndexIterator implements Iterator<Pair<String, Integer>> {
    private final Iterator<String> stringIterator;
    private final Iterator<Integer> integerIterator;

    ZippedWithIndexIterator(Iterator<String> stringIterator, Iterator<Integer> integerIterator) {
        this.stringIterator = stringIterator;
        this.integerIterator = integerIterator;
    }
    @Override
    public Pair<String, Integer> next() {
        return new Pair<>(stringIterator.next(), integerIterator.next());
    }
    @Override
    public boolean hasNext() {
        return stringIterator.hasNext() && integerIterator.hasNext();
    }
}

0

Collectors.reducingนี่เป็นตัวอย่างด้วย แต่ในที่สุดก็สร้างรายการต่อไป

Stream<String> lines = Arrays.asList("email", "aaa", "bbb", "ccc")
        .stream();

List reduceList = (List) lines
        .collect(Collectors.reducing( new ArrayList<String>(), (a, v) -> {
                    List list = (List) a;
                    if (!(list.isEmpty() && v.equals("email"))) {
                        list.add(v);
                    }
                    return a;
                }));

reduceList.forEach(System.out::println);

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