जावा 8 स्ट्रीम फॉरएच से ब्रेक या वापसी?


312

जब हम एक से अधिक बाहरी पुनरावृत्ति काIterable उपयोग करते हैं breakया returnप्रत्येक लूप के लिए बढ़ाया जाता है जैसे:

for (SomeObject obj : someObjects) {
   if (some_condition_met) {
      break; // or return obj
   }
}

हम जावा 8 लैम्ब्डा अभिव्यक्ति में आंतरिक पुनरावृत्ति का उपयोग breakया returnउपयोग कैसे कर सकते हैं:

someObjects.forEach(obj -> {
   //what to do here?
})

6
आप नहीं कर सकते। बस एक वास्तविक forबयान का उपयोग करें ।
बोवन


बेशक यह है के लिए संभव forEach()। समाधान अच्छा नहीं है, लेकिन यह है संभव। नीचे मेरा जवाब देखें।
होन्ज़ा ज़िदेक

एक और दृष्टिकोण पर विचार करें, आप बस कोड निष्पादित नहीं करना चाहते हैं , इसलिए, इच्छा के ifअंदर एक सरल शर्त forEachचाल चलेगी।
थॉमस डेकाक्स

जवाबों:


373

यदि आपको इसकी आवश्यकता है, तो आपको उपयोग नहीं करना चाहिए forEach, लेकिन धाराओं पर उपलब्ध अन्य तरीकों में से एक; कौन सा, इस पर निर्भर करता है कि आपका लक्ष्य क्या है।

उदाहरण के लिए, यदि इस लूप का लक्ष्य पहले तत्व को खोजना है जो कुछ विधेय से मेल खाता है:

Optional<SomeObject> result =
    someObjects.stream().filter(obj -> some_condition_met).findFirst();

(नोट: यह पूरे संग्रह को पुनरावृत्त नहीं करेगा, क्योंकि धाराओं का आलसी मूल्यांकन किया जाता है - यह पहली वस्तु पर रुकेगा जो स्थिति से मेल खाता है)।

यदि आप केवल जानना चाहते हैं कि क्या संग्रह में कोई तत्व है जिसके लिए स्थिति सही है, तो आप उपयोग कर सकते हैं anyMatch :

boolean result = someObjects.stream().anyMatch(obj -> some_condition_met);

7
यह तब काम करता है जब कोई वस्तु ढूंढना लक्ष्य था, लेकिन उस लक्ष्य को आमतौर पर returnएक findSomethingविधि से कथन द्वारा परोसा जाता है । breakआम तौर पर ऑपरेशन के समय लेने के साथ जुड़ा हुआ है ।
मार्को टोपोलनिक

1
@MarkoTopolnik हाँ, मूल पोस्टर ने हमें यह जानने के लिए पर्याप्त जानकारी नहीं दी है कि लक्ष्य क्या है; एक "टेक जबकि" दो उल्लेख के अलावा एक तीसरी संभावना है। (क्या धाराओं के साथ "लेते समय" करने का एक सरल तरीका है?)।
जेसपर

3
जब लक्ष्य व्यवहार को ठीक से कार्यान्वित करना हो तो क्या होगा? क्या सबसे अच्छी बात यह है कि हम केवल लैंबडा के अंदर एक रनटाइम अपवाद को फेंकने के लिए कर सकते हैं जब हम नोटिस करते हैं कि उपयोगकर्ता ने रद्द करने का अनुरोध किया है?
user2163960

1
@HonzaZidek एडिटेड, लेकिन बात यह नहीं है कि यह संभव है या नहीं, लेकिन चीजों को करने का सही तरीका क्या है। आपको इसके forEachलिए उपयोग करने का प्रयास नहीं करना चाहिए ; आपको इसके बजाय एक और, अधिक उपयुक्त विधि का उपयोग करना चाहिए।
जेसपर

1
@ जेस्पर मैं आपसे सहमत हूं, मैंने लिखा है कि मुझे "अपवाद समाधान" पसंद नहीं था। हालाँकि आपका शब्द "यह संभव नहीं हैforEach " तकनीकी रूप से गलत था। मैं आपके समाधान को भी प्राथमिकता देता हूं, हालांकि मैं उन मामलों का उपयोग करने की कल्पना कर सकता हूं जहां मेरे जवाब में दिए गए समाधान बेहतर हैं: जब वास्तविक अपवाद के कारण लूप को समाप्त किया जाना चाहिए । मैं इस बात से सहमत हूं कि प्रवाह को नियंत्रित करने के लिए आपको आमतौर पर अपवादों का उपयोग नहीं करना चाहिए ।
होन्ज़ा ज़िदेक

53

यह है के लिए संभव Iterable.forEach()है (लेकिन मज़बूती से नहीं के साथ Stream.forEach())। समाधान अच्छा नहीं है, लेकिन यह है संभव।

चेतावनी : आपको इसका उपयोग व्यावसायिक तर्क को नियंत्रित करने के लिए नहीं करना चाहिए, बल्कि विशुद्ध रूप से एक असाधारण स्थिति को संभालने के लिए करना चाहिए जो कि निष्पादन के दौरान होता है forEach()। जैसे कि एक संसाधन अचानक सुलभ होना बंद हो जाता है, संसाधित वस्तुओं में से एक अनुबंध का उल्लंघन कर रहा है (जैसे अनुबंध कहता है कि धारा में सभी तत्व नहीं होने चाहिए nullलेकिन अचानक और अप्रत्याशित रूप से उनमें से एक है)null ) आदि।

के लिए प्रलेखन के अनुसार Iterable.forEach():

Iterable जब तक सभी तत्वों को संसाधित नहीं किया जाता है तब तक प्रत्येक तत्व के लिए दी गई कार्रवाई करता है या कार्रवाई एक अपवाद को फेंक देती है ... कार्रवाई द्वारा फेंके गए अपवाद कॉल करने वाले को रिले किए जाते हैं।

तो आप एक अपवाद फेंकते हैं जो तुरंत आंतरिक लूप को तोड़ देगा।

कोड कुछ इस तरह होगा - मैं यह नहीं कह सकता कि मुझे यह पसंद है लेकिन यह काम करता है। आप अपनी खुद की क्लास बनाएँ BreakExceptionजो फैली हुई हो RuntimeException

try {
    someObjects.forEach(obj -> {
        // some useful code here
        if(some_exceptional_condition_met) {
            throw new BreakException();
       }
    }
}
catch (BreakException e) {
    // here you know that your condition has been met at least once
}

गौर करें कि द try...catch है नहीं आसपास लैम्ब्डा अभिव्यक्ति पूरे, बल्कि आसपास forEach()विधि। इसे अधिक दृश्यमान बनाने के लिए, कोड का निम्नलिखित प्रतिलेखन देखें जो इसे और अधिक स्पष्ट रूप से दिखाता है:

Consumer<? super SomeObject> action = obj -> {
    // some useful code here
    if(some_exceptional_condition_met) {
        throw new BreakException();
    }
});

try {
    someObjects.forEach(action);
}
catch (BreakException e) {
    // here you know that your condition has been met at least once
}

37
मुझे लगता है कि यह एक बुरा अभ्यास है और इसे समस्या का समाधान नहीं माना जाना चाहिए। यह खतरनाक है क्योंकि यह एक शुरुआत के लिए भ्रामक हो सकता है। प्रभावी जावा 2 संस्करण के अनुसार, अध्याय 9, आइटम 57: 'केवल असाधारण स्थितियों के लिए अपवादों का उपयोग करें'। इसके अलावा 'प्रोग्रामिंग त्रुटियों को इंगित करने के लिए रनटाइम अपवादों का उपयोग करें'। निश्चित रूप से, मैं @Jesper समाधान को देखने के लिए इस समाधान पर विचार करते हुए किसी को भी प्रोत्साहित करता हूं।
लुई एफ

15
@LouisF। मैंने स्पष्ट रूप से कहा "मैं यह नहीं कह सकता कि मुझे यह पसंद है लेकिन यह काम करता है"। ओपी ने पूछा "फोरआच () से कैसे टूटना है" और यह एक जवाब है। मैं इस बात से पूरी तरह सहमत हूं कि इसका इस्तेमाल व्यापारिक तर्क को नियंत्रित करने के लिए नहीं किया जाना चाहिए। हालांकि मैं कुछ उपयोगी उपयोग के मामलों की कल्पना कर सकता हूं, जैसे कि एक संसाधन का कनेक्शन अचानक forEach () या तो के बीच में उपलब्ध नहीं है, जिसके लिए अपवाद का उपयोग करना बुरा अभ्यास नहीं है। मैंने अपने उत्तर को स्पष्ट करने के लिए एक अनुच्छेद जोड़ा है।
होन्ज़ा ज़िदेक

1
मुझे लगता है कि यह एक अच्छा समाधान है। Google को "जावा अपवाद" के लिए और कुछ अन्य शब्दों के साथ "सर्वोत्तम प्रथाओं" या "अनियंत्रित", आदि की खोज करने के बाद, मैं देखता हूं कि अपवादों का उपयोग करने के तरीके पर विवाद है। मैंने अपने कोड में इस समाधान का उपयोग किया क्योंकि धारा एक मानचित्र का प्रदर्शन कर रही थी जिसमें मिनट लगेंगे। मैं चाहता था कि उपयोगकर्ता कार्य को रद्द करने में सक्षम हो इसलिए मैंने ध्वज "isUserCancelRequested" के लिए प्रत्येक गणना की शुरुआत में जाँच की और सत्य होने पर एक अपवाद को फेंक दिया। यह साफ है, अपवाद कोड कोड के छोटे हिस्से के लिए अलग है, और यह काम करता है।
जेसन

2
ध्यान दें कि फोन करने वाले के लिए अपवाद के बारे में समान मजबूत गारंटी प्रदान नहींStream.forEach करता है , इसलिए अपवाद को फेंकना इस तरह से काम करने की गारंटी नहीं है । Stream.forEach
रेडियोडैफ

1
@ रोडिफ़ एक मान्य बिंदु है, धन्यवाद। मूल पोस्ट के बारे में था Iterable.forEach(), लेकिन मैंने पूर्णता के लिए अपनी बात मेरे पाठ में जोड़ दी।
होनज़ा ज़िदेक

43

एक लैम्ब्डा में एक वापसी प्रत्येक के लिए एक जारी के बराबर होती है, लेकिन एक ब्रेक के बराबर नहीं है। आप जारी रखने के लिए बस एक वापसी कर सकते हैं:

someObjects.forEach(obj -> {
   if (some_condition_met) {
      return;
   }
})

2
सामान्य आवश्यकता को संबोधित करने के लिए अच्छा और मुहावरेदार समाधान। स्वीकृत उत्तर आवश्यकता को एक्सट्रपलेशन करता है।
davidxxx

6
दूसरे शब्दों में यह "जावा 8 स्ट्रीम फॉरएच से ब्रेक या रिटर्न" नहीं है, जो कि वास्तविक प्रश्न था
जारोस्लाव ज़ारूबा

1
यह अभी भी स्रोत स्ट्रीम के माध्यम से रिकॉर्ड "खींच" करेगा, जो कि खराब है यदि आप किसी प्रकार के दूरस्थ डेटासेट के माध्यम से पेजिंग कर रहे हैं।
एड्रियन बेकर

23

नीचे आपको एक परियोजना में उपयोग किए गए समाधान मिलते हैं। इसके बजाय forEachबस का उपयोग करें allMatch:

someObjects.allMatch(obj -> {
    return !some_condition_met;
});

11

या तो आपको एक विधि का उपयोग करने की आवश्यकता है जो एक विधेय का उपयोग करती है जो यह दर्शाता है कि क्या जारी रखना है (इसलिए इसके बजाय इसका ब्रेक है) या आपको एक अपवाद फेंकने की आवश्यकता है - जो कि एक बहुत ही बदसूरत दृष्टिकोण है।

तो आप forEachConditionalइस तरह से एक विधि लिख सकते हैं :

public static <T> void forEachConditional(Iterable<T> source,
                                          Predicate<T> action) {
    for (T item : source) {
        if (!action.test(item)) {
            break;
        }
    }
}

इसके बजाय Predicate<T>, आप अपने स्वयं के कार्यात्मक इंटरफ़ेस को उसी सामान्य विधि (कुछ लेने Tऔर वापस लौटने bool) के साथ परिभाषित करना चाह सकते हैं, लेकिन उन नामों के साथ जो अपेक्षा को अधिक स्पष्ट रूप से इंगित करते हैं - Predicate<T>यहां आदर्श नहीं है।


1
मेरा सुझाव है कि वास्तव में स्ट्रीम एपीआई और यहां एक कार्यात्मक दृष्टिकोण का उपयोग करना इस सहायक विधि को बनाने से बेहतर है यदि जावा 8 को किसी भी तरह से उपयोग किया जा रहा है।
स्किवि

3
यह क्लासिक takeWhileऑपरेशन है, और यह सवाल है, लेकिन उन लोगों में से एक है जो स्ट्रीम एपीआई में इसकी कमी महसूस करते हैं।
मार्को टोपोलनिक

2
@Marko: takeWhile को लगता है कि यह एक ऑपरेशन उपज आइटम होगा, प्रत्येक पर कार्रवाई नहीं। निश्चित रूप से LINQ में .NET में यह साइड-इफेक्ट के साथ एक कार्रवाई के साथ टेकहाइल का उपयोग करने के लिए खराब रूप होगा।
जॉन स्कीट

9

आप java8 + rxjava का उपयोग कर सकते हैं ।

//import java.util.stream.IntStream;
//import rx.Observable;

    IntStream intStream  = IntStream.range(1,10000000);
    Observable.from(() -> intStream.iterator())
            .takeWhile(n -> n < 10)
            .forEach(n-> System.out.println(n));

8
जावा 9 स्ट्रीम पर टेकवाइल ऑपरेशन के लिए समर्थन की पेशकश करेगा।
pisaruk

6

समानांतर संचालन में अधिकतम प्रदर्शन के लिए findAny () का उपयोग करें जो findFirst () के समान है।

Optional<SomeObject> result =
    someObjects.stream().filter(obj -> some_condition_met).findAny();

हालाँकि, यदि कोई स्थिर परिणाम वांछित है, तो इसके बजाय findFirst () का उपयोग करें।

यह भी ध्यान दें कि मिलान पैटर्न (anyMatch () / allMatch) केवल बूलियन लौटाएगा, आपको मिलान की गई वस्तु नहीं मिलेगी।


6

जावा 9+ के साथ अपडेट करें takeWhile:

MutableBoolean ongoing = MutableBoolean.of(true);
someobjects.stream()...takeWhile(t -> ongoing.value()).forEach(t -> {
    // doing something.
    if (...) { // want to break;
        ongoing.setFalse();
    }
});

3

मैंने कुछ इस तरह से हासिल किया है

  private void doSomething() {
            List<Action> actions = actionRepository.findAll();
            boolean actionHasFormFields = actions.stream().anyMatch(actionHasMyFieldsPredicate());
            if (actionHasFormFields){
                context.addError(someError);
            }
        }
    }

    private Predicate<Action> actionHasMyFieldsPredicate(){
        return action -> action.getMyField1() != null;
    }

3

आप झांकने (..) और anyMatch (..) के मिश्रण का उपयोग करके प्राप्त कर सकते हैं।

अपने उदाहरण का उपयोग करना:

someObjects.stream().peek(obj -> {
   <your code here>
}).anyMatch(obj -> !<some_condition_met>);

या बस एक सामान्य उपयोग विधि लिखें:

public static <T> void streamWhile(Stream<T> stream, Predicate<? super T> predicate, Consumer<? super T> consumer) {
    stream.peek(consumer).anyMatch(predicate.negate());
}

और फिर इसे इस तरह उपयोग करें:

streamWhile(someObjects.stream(), obj -> <some_condition_met>, obj -> {
   <your code here>
});

0

और इसका क्या:

final BooleanWrapper condition = new BooleanWrapper();
someObjects.forEach(obj -> {
   if (condition.ok()) {
     // YOUR CODE to control
     condition.stop();
   }
});

BooleanWrapperप्रवाह को नियंत्रित करने के लिए आपको एक वर्ग कहां होना चाहिए।


3
या एटॉमिक बुलियन?
कोकजे

1
यह तब वस्तु प्रसंस्करण को छोड़ देता है !condition.ok(), हालांकि यह forEach()सभी वस्तुओं पर वैसे भी लूपिंग को रोकता नहीं है। यदि धीमा भाग forEach()पुनरावृत्ति है और इसका उपभोक्ता नहीं है (उदाहरण के लिए, इसे धीमी नेटवर्क कनेक्शन से ऑब्जेक्ट मिलता है), तो यह दृष्टिकोण बहुत उपयोगी नहीं है।
जक्कम

हां आप सही हैं, इस मामले में मेरा जवाब काफी गलत है।
थॉमस डेकाक्स

0
int valueToMatch = 7;
Stream.of(1,2,3,4,5,6,7,8).anyMatch(val->{
   boolean isMatch = val == valueToMatch;
   if(isMatch) {
      /*Do whatever you want...*/
       System.out.println(val);
   }
   return isMatch;
});

यह केवल ऑपरेशन करेगा जहां यह मैच ढूंढता है, और मैच ढूंढने के बाद इसे रोकना पुनरावृत्ति है।


0

मैं किसी भी मैमैच का उपयोग करने का सुझाव दूंगा। उदाहरण:-

return someObjects.stream().anyMatch(obj -> 
    some_condition_met;
);

आप किसी भी जानकारी को समझने के लिए इस पोस्ट का उल्लेख कर सकते हैं: - https://beginnersbook.com/2017/11/java-8-stream-anymatch-example/

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.