पहले तत्व को विधेय द्वारा खोजें


504

मैंने अभी जावा 8 लैम्ब्डा के साथ खेलना शुरू किया है और मैं उन कुछ चीजों को लागू करने की कोशिश कर रहा हूं, जिन्हें मैं कार्यात्मक भाषाओं में उपयोग कर रहा हूं।

उदाहरण के लिए, अधिकांश कार्यात्मक भाषाओं में कुछ प्रकार के खोज फ़ंक्शन होते हैं, जो अनुक्रमों पर संचालित होते हैं, या पहले तत्व को लौटाने वाली सूचियों, जिसके लिए विधेय है true। जावा 8 में इसे प्राप्त करने का एकमात्र तरीका यह है:

lst.stream()
    .filter(x -> x > 5)
    .findFirst()

हालांकि यह मेरे लिए अक्षम है, क्योंकि फ़िल्टर पूरी सूची को स्कैन करेगा, कम से कम मेरी समझ में (जो गलत हो सकता है)। क्या कोई बेहतर तरीका है?


53
यह अक्षम नहीं है, जावा 8 स्ट्रीम कार्यान्वयन का आलसी मूल्यांकन किया जाता है, इसलिए फ़िल्टर केवल टर्मिनल ऑपरेशन पर लागू किया जाता है। यहाँ एक ही सवाल: stackoverflow.com/questions/21219667/stream-and-lazy-evaluation
मारेक ग्रेगर

1
ठंडा। यही मुझे उम्मीद है कि यह करेगा। यह अन्यथा एक प्रमुख डिजाइन फ्लॉप होता।
सिक्की

2
यदि आपका इरादा वास्तव में यह जांचने का है कि सूची में ऐसा कोई तत्व है या नहीं (संभवत: पहले कई में से एक भी नहीं) .findAny () सैद्धांतिक रूप से एक समांतर सेटिंग में अधिक कुशल हो सकता है, और निश्चित रूप से उस इरादे को अधिक स्पष्ट रूप से बताता है।
जोआचिम लूज़

एक साधारण फोरचच चक्र की तुलना में, यह ढेर पर कई वस्तुओं का निर्माण करेगा और दर्जनों गतिशील विधि कॉल करेगा। हालांकि यह आपके प्रदर्शन परीक्षणों में हमेशा नीचे की रेखा को प्रभावित नहीं कर सकता है, गर्म स्थानों में स्ट्रीम और इसी तरह के हैवीवेट निर्माणों के तुच्छ उपयोग से इसे अलग करने का फर्क पड़ता है।
आगस्टोन होर्वाथ

जवाबों:


720

नहीं, फ़िल्टर संपूर्ण स्ट्रीम को स्कैन नहीं करता है। यह एक मध्यवर्ती ऑपरेशन है, जो एक आलसी स्ट्रीम (वास्तव में सभी मध्यवर्ती संचालन एक आलसी स्ट्रीम) लौटाता है। आपको समझाने के लिए, आप बस निम्नलिखित परीक्षण कर सकते हैं:

List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);
int a = list.stream()
            .peek(num -> System.out.println("will filter " + num))
            .filter(x -> x > 5)
            .findFirst()
            .get();
System.out.println(a);

कौन से आउटपुट:

will filter 1
will filter 10
10

आप देखते हैं कि धारा के केवल दो पहले तत्व वास्तव में संसाधित होते हैं।

तो आप अपने दृष्टिकोण के साथ जा सकते हैं जो पूरी तरह से ठीक है।


37
एक नोट के रूप में, मैंने get();यहां उपयोग किया क्योंकि मुझे पता है कि मैं स्ट्रीम पाइप लाइन को किन मूल्यों को खिलाता हूं और इसलिए इसका एक परिणाम होगा। व्यवहार में, आपको उपयोग नहीं करना चाहिए get();, लेकिन orElse()/ orElseGet()/ orElseThrow()(एक एनएसईई के बजाय अधिक सार्थक त्रुटि के लिए) जैसा कि आप नहीं जानते होंगे कि क्या स्ट्रीम पाइपलाइन पर लागू किए गए कार्यों के परिणामस्वरूप एक तत्व होगा।
एलेक्सिस सी।

31
.findFirst().orElse(null);उदाहरण के लिए
गोंडी

20
Orlice null का उपयोग न करें। यह एक विरोधी पैटर्न होना चाहिए। यह सब वैकल्पिक में शामिल है इसलिए आपको एनपीई का जोखिम क्यों उठाना चाहिए? मुझे लगता है कि वैकल्पिक से निपटना बेहतर तरीका है। उपयोग करने से पहले बस isPresent () के साथ वैकल्पिक का परीक्षण करें।
BeJay

@BeJay मुझे समझ में नहीं आता है। इसके बजाय मुझे क्या उपयोग करना चाहिए orElse?
जॉन हेन्केल

3
@ जॉनहोनेल मुझे लगता है कि बेजे का मतलब यह है कि आपको इसे एक Optionalप्रकार के रूप में छोड़ देना चाहिए , जो कि .findFirstरिटर्न है। वैकल्पिक के उपयोगों में से एक है डेवलपर्स nullको चेक के बजाय सेग से निपटने से बचने में मदद करने के लिए myObject != null, आप myOptional.isPresent()वैकल्पिक इंटरफ़ेस के अन्य भागों की जांच या उपयोग कर सकते हैं । क्या इससे यह स्पष्ट हुआ?
AMTerp

102

हालाँकि यह मेरे लिए अक्षम है, क्योंकि फ़िल्टर पूरी सूची को स्कैन करेगा

नहीं, यह नहीं होगा - यह "तोड़" होगा जैसे ही पहला तत्व विधेय को संतुष्ट करता है। आप विशेष रूप से स्ट्रीम पैकेज जावदोक में आलस्य के बारे में अधिक पढ़ सकते हैं (मेरा जोर):

कई स्ट्रीम ऑपरेशन, जैसे कि फ़िल्टरिंग, मैपिंग या डुप्लिकेट निष्कासन, आलस्य को लागू किया जा सकता है, अनुकूलन के अवसरों को उजागर कर सकता है। उदाहरण के लिए, "लगातार तीन स्वरों के साथ पहला स्ट्रिंग ढूंढें" सभी इनपुट स्ट्रिंग्स की जांच करने की आवश्यकता नहीं है। स्ट्रीम संचालन को मध्यवर्ती (स्ट्रीम-उत्पादक) संचालन और टर्मिनल (मान- या साइड-इफ़ेक्ट-प्रोडक्शन) संचालन में विभाजित किया गया है। इंटरमीडिएट ऑपरेशन हमेशा आलसी होते हैं।


5
यह उत्तर मेरे लिए अधिक जानकारीपूर्ण था, और यही बताता है कि क्यों, न केवल कैसे। मैं कभी भी नए मध्यवर्ती संचालन हमेशा आलसी नहीं होता; जावा स्ट्रीम मुझे आश्चर्यचकित करती है।
केविनपेयर

30
return dataSource.getParkingLots()
                 .stream()
                 .filter(parkingLot -> Objects.equals(parkingLot.getId(), id))
                 .findFirst()
                 .orElse(null);

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


बेहतर: चूंकि हम बूलियन रिटर्न मान की तलाश कर रहे हैं, इसलिए हम इसे शून्य चेक जोड़कर बेहतर कर सकते हैं: dataSource.getParkingLots () (स्ट्रीम) () फ़िल्टर (पार्किंगलॉट -> Objects.equals (Parkingotot.getId (), id)) .findFirst ()। orElse (null)! = null;
श्रीधर भाट

1
@ श्रीधरभट आपको करने की आवश्यकता नहीं है .orElse(null) != null। इसके बजाय, वैकल्पिक एपीआई का उपयोग करें .isPresentअर्थात् .findFirst().isPresent()
AMTerp

@ ओपी के पहले श्रीधरभट को बूलियन रिटर्न वैल्यू की तलाश नहीं थी। यदि वे थे, तो दूसरा यह लिखना साफ होगा.stream().map(ParkingLot::getId).anyMatch(Predicate.isEqual(id))
अजाक्सलेयंग

13

के अलावा एलेक्सिस सी के जवाब है, आप एक सरणी सूची के साथ काम कर रहे हैं, जिसमें आप यकीन नहीं है कि तत्व आप मौजूद है के लिए खोज रहे हैं, इस का उपयोग नहीं कर रहे हैं।

Integer a = list.stream()
                .peek(num -> System.out.println("will filter " + num))
                .filter(x -> x > 5)
                .findFirst()
                .orElse(null);

तो फिर तुम बस की जांच कर सकता है कि क्या एक है null


1
आपको अपना उदाहरण ठीक करना चाहिए। आप एक सादे int को null असाइन नहीं कर सकते। stackoverflow.com/questions/2254435/can-an-int-be-null-in-java
RubioRic

मैंने आपकी पोस्ट संपादित कर दी है। जब आप पूर्णांकों की सूची में खोज कर रहे होते हैं तो 0 (शून्य) एक मान्य परिणाम हो सकता है। पूर्णांक द्वारा बदला हुआ चर प्रकार और अशक्त द्वारा डिफ़ॉल्ट मान।
RubioRic

0

import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

// Stream is ~30 times slower for same operation...
public class StreamPerfTest {

    int iterations = 100;
    List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);


    // 55 ms
    @Test
    public void stream() {

        for (int i = 0; i < iterations; i++) {
            Optional<Integer> result = list.stream()
                    .filter(x -> x > 5)
                    .findFirst();

            System.out.println(result.orElse(null));
        }
    }

    // 2 ms
    @Test
    public void loop() {

        for (int i = 0; i < iterations; i++) {
            Integer result = null;
            for (Integer walk : list) {
                if (walk > 5) {
                    result = walk;
                    break;
                }
            }
            System.out.println(result);
        }
    }
}

0

बेहतर वन-लाइनर उत्तर: यदि आप एक बुलियन रिटर्न वैल्यू की तलाश में हैं, तो हम इसे बेहतर तरीके से जोड़ सकते हैं।

return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().isPresent();

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