सशर्त रूप से स्ट्रीम से पहले (शून्य इंडेक्स के साथ) तत्व निकालें


10

मेरे पास निम्नलिखित कोड हैं:

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

अगर मुट्ठी स्ट्रिंग के बराबर है, तो "email"मैं स्ट्रीम से पहला स्ट्रिंग निकालना चाहता हूं। स्ट्रीम से अन्य स्ट्रिंग्स के लिए मुझे इस चेक की आवश्यकता नहीं है।

मैं इसे कैसे कर सकता था?

पुनश्च

यकीन है कि मैं इसे सूची में बदल सकता हूं, फिर पुराने स्कूल को लूप के लिए उपयोग कर सकता हूं लेकिन आगे मुझे फिर से स्ट्रीम की आवश्यकता है।


3
और अगर दूसरा तत्व "ईमेल" है तो आप इसे नहीं छोड़ना चाहते हैं?
माइकल

@michalk आप सही हैं
gstackoverflow

हम्म ... वहाँ है skip(long n)कि पहले nतत्वों को छोड़ देता है , लेकिन मुझे नहीं पता कि क्या आप इसे किसी तरह से कंडीशन कर सकते हैं ...
deHaar

4
@gstackoverflow अगर यह संभावना नहीं है कि स्ट्रीम में पहले दो तार "ईमेल" के बराबर हैं, तो मुझे लगता है कि अगर आप सीएसवी फ़ाइल के हेडर के बारे में बात कर रहे हैं, तो आप उपयोग कर सकते हैंstream.dropWhile(s -> s.equals("email"));
इरिट्रिया

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);

जो मेरी राय में सबसे साफ समाधान है। ध्यान दें कि यह जावा 9 के समान नहीं है dropWhile, जो मेल खाते हैं तो एक से अधिक लाइन छोड़ देगा।


सहमत - यह समाधान बेहतर है लेकिन dropWhile - दूसरा स्थान है। दुर्भाग्य से लेखक ने इस विचार को एक अलग उत्तर के रूप में पोस्ट नहीं करने का निर्णय लिया
gstackoverflow

3

यदि आपके पास सूची नहीं है और इसे केवल ए के साथ ही करना है Stream, तो आप इसे एक चर के साथ कर सकते हैं।

बात यह है कि आप केवल एक चर का उपयोग कर सकते हैं यदि यह "अंतिम" या "प्रभावी रूप से अंतिम" है तो आप शाब्दिक बूलियन का उपयोग नहीं कर सकते हैं। आप अभी भी इसे एक के साथ कर सकते हैं 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"))हालांकि बहस कर सकता है ।
जोप इगेनेन

1
predicateस्टेटलेस होना चाहिए
१२

3
यदि AtomicBooleanयहां का उपयोग समानांतर धाराओं के लिए पूरा करना है (जैसा कि सुझावों का उपयोग compareAndSetहै) तो यह पूरी तरह से गलत है क्योंकि यह केवल तभी काम करेगा जब धारा अनुक्रमिक हो। यदि आप तकनीक का उपयोग करते हैं, stream.sequential().filter(...)तो मैं मानता हूं कि यह काम करेगा।
डेनियल

3
@daniel आप सही हैं, यह दृष्टिकोण समानांतर प्रसंस्करण का समर्थन नहीं करते हुए एक थ्रेड सुरक्षित निर्माण (प्रत्येक तत्व के लिए) का खर्च दे रहा है। राज्य के फिल्टर के साथ इतने सारे उदाहरण इन वर्गों का उपयोग करने का कारण यह है कि थ्रेड सुरक्षा के बिना कोई समान नहीं है और लोग अपने उत्तरों में एक समर्पित वर्ग बनाने की जटिलता से बचते हैं ...
होल्गर

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());

जावा 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

@ अरण्य उत्तर सही है। आप पहली पंक्ति के लिए एक स्ट्रीम बना सकते हैं और फिर नीचे की तरह,

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

यहां तक ​​कि अगर आप एक फिल्टर का उपयोग करते हैं, तो यह प्रत्येक पंक्ति के लिए गणना की जाएगी। विशाल फ़ाइल के मामले में, यह प्रदर्शन का कारण हो सकता है। सीमा के मामले में, इसकी तुलना केवल एक बार की जाएगी।
कोड_मोड

एक पाठक के लिए काम नहीं करने के अलावा, limit(0)निश्चित रूप से होना चाहिए limit(1)...
Holger

मैंने टेस्टिम के बाद सीमा 0 का उत्तर संपादित किया है।
कोड_मोड

1
मैं देखता हूं कि आपने इसे बदल दिया है, लेकिन .limit(0).filter(line -> !line.startsWith("email"))कोई मतलब नहीं है। विधेय का मूल्यांकन कभी नहीं किया जाएगा। स्ट्रीम स्ट्रीम को पुन: प्रस्तुत करने में सक्षम धारा स्रोत के लिए, limit(1)पहले और skip(1)दूसरे पर संयोजन सही होगा। एक पाठक की तरह एक स्ट्रीम स्रोत के लिए, न limit(1)है और न ही limit(0)काम करेंगे। आपने अभी-अभी बदला है कि कौन सी रेखा बिना शर्त निगल जाती है। यहां तक ​​कि अगर आपको एक संयोजन मिला जो वांछित काम करने के लिए होता है, तो यह अनिर्दिष्ट, कार्यान्वयन पर निर्भर व्यवहार की जमीन पर होगा।
होल्गर

0

तत्वों को उनके सूचकांक के आधार पर फ़िल्टर करने के लिए, आप AtomicIntegerप्रसंस्करण करते समय सूचकांक को संग्रहित करने और बढ़ाने के लिए उपयोग कर सकते हैं 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>ऐसा इंडेक्स बना सकते हैं जो इंडेक्स का प्रतिनिधित्व करेगा और इसे आपके साथ "ज़िप" करेगाStream<String> बनाने के लिए एकStream<Pair<String, Integer>>

फिर इंडेक्स का उपयोग करके फ़िल्टर करें और इसे वापस मैप करें a 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);

0

इसे इस्तेमाल करे:

MutableBoolean isFirst = MutableBoolean.of(true);
lines..dropWhile(e -> isFirst.getAndSet(false) && "email".equals(e))
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.