क्यों करता है फ़र्स्ट () एक NullPointerException को फेंकता है यदि पहला तत्व जो पाता है वह अशक्त है?


89

यह क्यों फेंकता है java.lang.NullPointerException?

List<String> strings = new ArrayList<>();
        strings.add(null);
        strings.add("test");

        String firstString = strings.stream()
                .findFirst()      // Exception thrown here
                .orElse("StringWhenListIsEmpty");
                //.orElse(null);  // Changing the `orElse()` to avoid ambiguity

में पहला आइटम stringsहै null, जो पूरी तरह से स्वीकार्य मूल्य है। इसके अलावा, findFirst()एक वैकल्पिक रिटर्न देता है , जो एस findFirst()को संभालने में सक्षम होने के लिए और भी अधिक समझ में आता है null

संपादित करें: orElse()कम अस्पष्ट होने के लिए अद्यतन किया गया।


4
नल पूरी तरह से स्वीकार्य मूल्य नहीं है ... इसके बजाय "" का उपयोग करें
मिशैल लैकोर्टे सेप

1
@MicheleLacorte हालांकि मैं Stringयहाँ उपयोग कर रहा हूँ , क्या होगा यदि यह एक सूची है जो DB में एक कॉलम का प्रतिनिधित्व करता है? उस स्तंभ के लिए पहली पंक्ति का मान हो सकता है null
neverendingqs

हां, लेकिन जावा में शून्य स्वीकार्य नहीं
है..उपयोगी

10
@MicheleLacorte, nullजावा में एक आम तौर पर स्वीकार्य मूल्य है, आम तौर पर बोल रहा हूँ। विशेष रूप से, यह एक के लिए एक मान्य तत्व है ArrayList<String>। किसी भी अन्य मूल्य की तरह, हालांकि, इसके साथ क्या किया जा सकता है, इसकी सीमाएं हैं। "कभी उपयोग न करें null" उपयोगी सलाह नहीं है, क्योंकि आप इससे बच नहीं सकते।
जॉन बोलिंगर

@NathanHughes - मुझे संदेह है कि जब तक आप कॉल करेंगे findFirst(), तब तक कुछ और नहीं जो आप करना चाहते हैं।
21-03 पर 21

जवाबों:


72

इसका कारण Optional<T>रिटर्न में उपयोग है । वैकल्पिक को सम्‍मिलित करने की अनुमति नहीं है null। अनिवार्य रूप से, यह "यह वहां नहीं है" और "यह वहां नहीं है" स्थितियों को भेद करने का कोई तरीका प्रदान करता है, लेकिन यह null" सेट " है।

यही कारण है कि प्रलेखन स्पष्ट रूप से स्थिति का निषेध करता है जब इसमें nullचुना जाता है findFirst():

फेंकता:

NullPointerException - यदि चयनित तत्व है null


3
यह ट्रैक करना आसान होगा यदि उदाहरण के अंदर एक निजी बूलियन के साथ एक मूल्य मौजूद है Optional। वैसे भी, मुझे लगता है कि मैं शेख़ी मार रहा हूं - अगर भाषा इसका समर्थन नहीं करती है, तो वह इसका समर्थन नहीं करती है।
neverendingqs

2
@neverendingqs बिल्कुल, booleanइन दो स्थितियों को अलग करने के लिए एक का उपयोग करना सही अर्थ होगा। मेरे लिए, ऐसा लगता है कि Optional<T>यहाँ का उपयोग एक संदिग्ध विकल्प था।
सेर्गेई कालिनिचेंको

1
@neverendingqs मैं किसी भी अच्छे दिखने वाले विकल्प के बारे में नहीं सोच सकता, इसके अलावा अपने स्वयं के नल को रोल करना , जो या तो आदर्श नहीं है।
सेर्गेई कलिनिचेंको

1
मैंने एक निजी विधि लिखना समाप्त कर दिया है जो किसी भी Iterableप्रकार, जाँच hasNext(), और उचित मूल्य से पुनरावृत्ति प्राप्त करता है ।
neverendingqs

1
मुझे लगता है कि इसे और अधिक समझ में आता है, तो findFirstरिटर्न पीओ के मामले में एक खाली वैकल्पिक मूल्य
डैनी

47

जैसा कि पहले से ही चर्चा है , एपीआई डिजाइनर यह नहीं मानते हैं कि डेवलपर nullउसी तरह से मूल्यों और अनुपस्थित मूल्यों का इलाज करना चाहता है ।

यदि आप अभी भी ऐसा करना चाहते हैं, तो आप अनुक्रम लागू करके इसे स्पष्ट रूप से कर सकते हैं

.map(Optional::ofNullable).findFirst().flatMap(Function.identity())

धारा के लिए। परिणाम दोनों मामलों में एक खाली वैकल्पिक होगा, अगर कोई पहला तत्व नहीं है या यदि पहला तत्व है null। तो आपके मामले में, आप उपयोग कर सकते हैं

String firstString = strings.stream()
    .map(Optional::ofNullable).findFirst().flatMap(Function.identity())
    .orElse(null);

एक nullमूल्य प्राप्त करने के लिए यदि पहला तत्व या तो अनुपस्थित है या null

यदि आप इन मामलों में अंतर करना चाहते हैं, तो आप बस इस flatMapकदम को छोड़ सकते हैं :

Optional<String> firstString = strings.stream()
    .map(Optional::ofNullable).findFirst().orElse(null);
System.out.println(firstString==null? "no such element":
                   firstString.orElse("first element is null"));

यह आपके अपडेट किए गए प्रश्न से बहुत भिन्न नहीं है। तुम बस बदलने के लिए "no such element"के साथ "StringWhenListIsEmpty"और "first element is null"साथ null। लेकिन अगर आप सशर्त पसंद नहीं करते हैं, तो आप इसे भी प्राप्त कर सकते हैं जैसे:

String firstString = strings.stream().skip(0)
    .map(Optional::ofNullable).findFirst()
    .orElseGet(()->Optional.of("StringWhenListIsEmpty"))
    .orElse(null);

अब, firstStringहो जाएगा nullएक तत्व मौजूद है, लेकिन है अगर nullऔर यह हो जाएगा "StringWhenListIsEmpty"जब कोई तत्व मौजूद है।


क्षमा करें, मुझे एहसास हुआ कि मेरे प्रश्न का अर्थ यह हो सकता है कि मैं nullया तो 1 के लिए लौटना चाहता था ) पहला तत्व है nullया 2) सूची के अंदर कोई तत्व मौजूद नहीं है। मैंने अस्पष्टता को दूर करने के लिए प्रश्न को अद्यतन किया है।
neverendingqs

1
3rd कोड स्निपेट में, Optionalको सौंपा जा सकता है null। चूंकि Optional"मान" प्रकार माना जाता है, इसलिए इसे कभी भी अशक्त नहीं होना चाहिए। और एक वैकल्पिक की तुलना कभी नहीं की जानी चाहिए ==। जावा 10 में कोड विफल हो सकता है :) या जब-जब मूल्य जावा में पेश किया जाता है।
ZhongYu

1
@ bayou.io: प्रलेखन यह नहीं कहता है कि मूल्य प्रकारों के संदर्भ नहीं हो सकते हैं nullऔर उदाहरणों की तुलना कभी नहीं की जानी चाहिए ==, संदर्भ nullका उपयोग करने के लिए परीक्षण किया जा सकता है ==क्योंकि इसके लिए परीक्षण करने का एकमात्र तरीका है null। मैं यह नहीं देख सकता कि nullमौजूदा कोड के लिए इस तरह के "कभी नहीं " कैसे काम करना चाहिए क्योंकि सभी उदाहरण चर और सरणी तत्वों के लिए डिफ़ॉल्ट मान भी है null। स्निपेट निश्चित रूप से सबसे अच्छा कोड नहीं है, लेकिन न nullही वर्तमान मानों के रूप में इलाज का कार्य है ।
होल्गर

जॉन गुलाब देखें - और न ही इसकी तुलना "==" ऑपरेटर से की जा सकती है, एक अशक्त के साथ भी नहीं
ZhongYu

1
चूंकि यह कोड एक जेनेरिक एपीआई का उपयोग कर रहा है, यह वह अवधारणा है जिसे एक बॉक्सिंग प्रतिनिधित्व कहते हैं null। हालाँकि, इस तरह की काल्पनिक भाषा के बदलाव के कारण कंपाइलर को यहां एक त्रुटि जारी करने (चुपचाप कोड को नहीं तोड़ने) का कारण होगा, मैं इस तथ्य के साथ रह सकता हूं, कि संभवतः इसे जावा 10 के लिए अनुकूलित किया जाएगा। मुझे लगता है, Streamएपीआई होगा तब काफी अलग दिखते हैं ...
Holger

18

java.util.Objects.nonNullखोजने से पहले आप सूची को फ़िल्टर करने के लिए उपयोग कर सकते हैं

कुछ इस तरह

list.stream().filter(Objects::nonNull).findFirst();

1
मैं चाहता हूँ firstStringहोने के लिए nullकरता है, तो पहला आइटम में stringsहै null
neverendingqs

2
दुर्भाग्य से यह उपयोग करता है Optional.ofजो शून्य सुरक्षित नहीं है। आप उपयोग कर सकते mapहैं Optional.ofNullable और फिर उपयोग कर सकते हैं findFirstलेकिन आप वैकल्पिक के एक विकल्प के साथ समाप्त हो जाएंगे
मोटोस

14

निम्न कोड के findFirst()साथ बदल देता है limit(1)और orElse()साथ बदल देता है reduce():

String firstString = strings.
   stream().
   limit(1).
   reduce("StringWhenListIsEmpty", (first, second) -> second);

limit()केवल 1 तत्व तक पहुंचने की अनुमति देता है reduceBinaryOperatorके लिए पारित reduceरिटर्न कि 1 तत्व वरना "StringWhenListIsEmpty"कोई तत्व तक पहुंच रहे हैं reduce

इस समाधान की सुंदरता यह है कि Optionalआवंटित नहीं किया गया है और BinaryOperatorलैम्ब्डा कुछ भी आवंटित नहीं करने वाला है।


1

वैकल्पिक को "मान" प्रकार माना जाता है। (में बारीक अक्षरों को पढ़ने जावाडोक :) JVM भी सब बदल सकते Optional<Foo>बस के साथ Fooसभी मुक्केबाजी और unboxing लागत को हटाने,। एक nullफू का मतलब है एक खाली Optional<Foo>

बूलियन ध्वज को जोड़ने के बिना, शून्य मान के साथ वैकल्पिक अनुमति देने के लिए यह एक संभावित डिज़ाइन है - बस एक प्रहरी वस्तु जोड़ें। ( thisप्रहरी के रूप में भी उपयोग कर सकते हैं ; थ्रोबेबल देखें। देखें)

निर्णय कि वैकल्पिक शून्य को लपेट नहीं सकता है रनटाइम लागत पर आधारित नहीं है। यह एक बेहद संरक्षित मुद्दा था और आपको मेलिंग सूचियों को खोदना होगा। निर्णय हर किसी के लिए आश्वस्त नहीं है।

किसी भी मामले में, चूंकि वैकल्पिक शून्य मान को लपेट नहीं सकता है, यह हमें जैसे मामलों में एक कोने में धकेल देता है findFirst। उन्होंने तर्क दिया होगा कि अशक्त मूल्य बहुत दुर्लभ हैं (यह भी माना जाता था कि स्ट्रीम को शून्य मानों को बार करना चाहिए), इसलिए रिक्त धाराओं के बजाय अशक्त मूल्यों पर अपवाद को फेंकना अधिक सुविधाजनक है।

एक वर्कअराउंड बॉक्स के लिए है null, उदा

class Box<T>
    static Box<T> of(T value){ .. }

Optional<Box<String>> first = stream.map(Box::of).findFirst();

(वे कहते हैं कि हर OOP समस्या का समाधान एक अन्य प्रकार शुरू करना है :)


1
दूसरा Boxप्रकार बनाने की कोई आवश्यकता नहीं है । Optionalप्रकार ही इस उद्देश्य पूरा कर सकते हैं। एक उदाहरण के लिए मेरा जवाब देखें ।
होल्गर

@ होलगर - हाँ, लेकिन यह भ्रमित करने वाला हो सकता है क्योंकि यह वैकल्पिक का उद्देश्य नहीं है। ओपी के मामले में, nullकिसी अन्य की तरह एक वैध मूल्य है, इसके लिए कोई विशेष उपचार नहीं है। (कुछ समय बाद तक :)
झोंगयु
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.