जावा 8 स्ट्रीम का .min () और .max (): यह संकलन क्यों करता है?


215

नोट: यह प्रश्न एक मृत लिंक से उत्पन्न होता है, जो पिछले SO प्रश्न था, लेकिन यहाँ जाता है ...

इस कोड को देखें ( ध्यान दें: मुझे पता है कि यह कोड "काम नहीं करेगा" और Integer::compareइसका उपयोग किया जाना चाहिए - मैंने अभी इसे लिंक किए गए प्रश्न से निकाला है :

final ArrayList <Integer> list 
    = IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());

System.out.println(list.stream().max(Integer::max).get());
System.out.println(list.stream().min(Integer::min).get());

के javadoc .min()और के अनुसार .max(), दोनों का तर्क होना चाहिए Comparator। फिर भी यहाँ विधि संदर्भ Integerकक्षा के स्थिर तरीकों के लिए हैं ।

तो, यह संकलन क्यों करता है?


6
ध्यान दें कि यह ठीक से काम नहीं करता है, इसे और के Integer::compareबजाय का उपयोग करना चाहिए । Integer::maxInteger::min
क्रिस्टोफ़र हैमरस्ट्रॉम

@ ChristofferHammarström मुझे पता है कि; ध्यान दें कि मैंने कोड निकालने से पहले कैसे कहा "मुझे पता है, यह बेतुका है"
fge

3
मैं आपको ठीक करने की कोशिश नहीं कर रहा था, मैं सामान्य रूप से लोगों को बता रहा हूं। आपने इसे ध्वनि के रूप में मान लिया है कि आपको लगता है कि जो हिस्सा बेतुका है वह तरीके Integerनहीं हैं Comparator
क्रिस्टोफर हैमरस्ट्रॉम

जवाबों:


242

मुझे स्पष्ट करें कि यहाँ क्या हो रहा है, क्योंकि यह स्पष्ट नहीं है!

सबसे पहले, Stream.max()एक उदाहरण को स्वीकार करता है Comparatorताकि स्ट्रीम में वस्तुओं को न्यूनतम या अधिकतम खोजने के लिए एक दूसरे के खिलाफ तुलना की जा सके, कुछ इष्टतम क्रम में जिन्हें आपको बहुत अधिक चिंता करने की आवश्यकता नहीं है।

तो सवाल यह है कि बेशक, क्यों Integer::maxस्वीकार किया जाता है? सब के बाद यह एक तुलनित्र नहीं है!

उत्तर इस तरह से है कि नया लैम्ब्डा कार्यक्षमता जावा 8 में काम करता है। यह एक अवधारणा पर निर्भर करता है जिसे अनौपचारिक रूप से "एकल सार पद्धति" इंटरफेस, या "एसएएम" इंटरफेस के रूप में जाना जाता है। विचार यह है कि एक सार पद्धति के साथ किसी भी इंटरफ़ेस को किसी भी लैम्ब्डा - या विधि संदर्भ द्वारा स्वचालित रूप से लागू किया जा सकता है - जिसका विधि हस्ताक्षर इंटरफ़ेस पर एक विधि के लिए एक मैच है। तो Comparatorइंटरफ़ेस की जांच करना (सरल संस्करण):

public Comparator<T> {
    T compare(T o1, T o2);
}

यदि कोई विधि खोज रही है Comparator<Integer>, तो यह अनिवार्य रूप से इस हस्ताक्षर की तलाश में है:

int xxx(Integer o1, Integer o2);

मैं "xxx" का उपयोग करता हूं क्योंकि विधि का नाम मिलान के उद्देश्यों के लिए उपयोग नहीं किया जाता है

इसलिए, दोनों काफी करीब हैं Integer.min(int a, int b)और Integer.max(int a, int b)ऑटोबॉक्सिंग इसे Comparator<Integer>एक विधि संदर्भ में प्रकट होने की अनुमति देगा ।


28
या वैकल्पिक रूप से list.stream().mapToInt(i -> i).max().get():।
assylias

13
@assylias आप के .getAsInt()बजाय उपयोग करना चाहते हैं get(), क्योंकि आप एक के साथ काम कर रहे हैं OptionalInt
स्किवि

... जब हम जो केवल कोशिश कर रहे हैं वह एक max()फ़ंक्शन के लिए एक कस्टम तुलनित्र प्रदान करना है!
मनु ३४३ Man२६

यह ध्यान देने योग्य है कि यह "एसएएम इंटरफ़ेस" वास्तव में "कार्यात्मक इंटरफ़ेस" कहलाता है और Comparatorदस्तावेज़ीकरण को देखते हुए हम देख सकते हैं कि यह एनोटेशन के साथ सजाया गया है @FunctionalInterface। यह डेकोरेटर जादू है जो अनुमति देता है Integer::maxऔर Integer::minइसे एक में परिवर्तित किया जा सकता है Comparator
क्रिस केरेक्स ने

2
@ChrisKerekes डेकोरेटर @FunctionalInterfaceमुख्य रूप से केवल दस्तावेज़ीकरण उद्देश्यों के लिए है, क्योंकि कंपाइलर एक एकल सार पद्धति के साथ किसी भी इंटरफ़ेस के साथ खुशी से ऐसा कर सकता है।
इरेटलिंग्लिस्ट

117

Comparatorएक कार्यात्मक इंटरफ़ेस है , और Integer::maxउस इंटरफ़ेस का अनुपालन करता है (ऑटोबॉक्सिंग / अनबॉक्सिंग के बाद विचार किया जाता है)। यह दो intमान लेता है और एक रिटर्न देता है int- जैसा कि आप Comparator<Integer>(फिर से, इंटेगर / इंट अंतर को अनदेखा करने के लिए स्क्वीटिंग) की अपेक्षा करेंगे ।

हालाँकि, मैं इसे सही काम करने की उम्मीद नहीं करूँगा, यह देखते हुए कि Integer.maxयह शब्दार्थ के अनुरूप नहीं है Comparator.compare। और वास्तव में यह वास्तव में सामान्य रूप से काम नहीं करता है। उदाहरण के लिए, एक छोटा परिवर्तन करें:

for (int i = 1; i <= 20; i++)
    list.add(-i);

... और अब maxमान -20 है और minमान -1 है।

इसके बजाय, दोनों कॉल का उपयोग करना चाहिए Integer::compare:

System.out.println(list.stream().max(Integer::compare).get());
System.out.println(list.stream().min(Integer::compare).get());

1
मैं कार्यात्मक इंटरफेस के बारे में जानता हूं; और मुझे पता है कि यह गलत परिणाम क्यों देता है। मुझे आश्चर्य है कि कैसे पृथ्वी पर संकलक मुझ पर चिल्लाना नहीं था
fge

6
@fge: ठीक है यह आप के बारे में अस्पष्ट थे unboxing था? (मैं वास्तव में उस हिस्से में देखा है नहीं।) एक Comparator<Integer>होता है int compare(Integer, Integer)... यह मन- boggling नहीं है कि जावा के लिए एक विधि के संदर्भ की अनुमति देता है int max(int, int)कि कन्वर्ट करने के लिए ...
जॉन स्कीट

7
@fge: संकलक के शब्दार्थ को जानना चाहिए Integer::max? इसके नजरिए से आप एक ऐसे फ़ंक्शन से गुज़रे, जो इसके स्पेसिफिकेशन से मिलता है, बस इतना ही चल सकता है।
मार्क पीटर्स

6
@fge: विशेष रूप से, अगर आप समझ हिस्सा क्या हो रहा है की, लेकिन इसके बारे में एक विशिष्ट पहलू के बारे में intrigued रहे हैं, इसके लायक बनाने कि से बचने के लिए लोगों को सवाल में स्पष्ट अपने समय बिट्स आप पहले से ही पता समझा बर्बाद कर रहे हैं।
जॉन स्कीट

20
मुझे लगता है कि अंतर्निहित समस्या का प्रकार हस्ताक्षर है Comparator.compare। यह एक लौटना चाहिए enumकी {LessThan, GreaterThan, Equal}है, न कि int। इस तरह, कार्यात्मक इंटरफ़ेस वास्तव में मेल नहीं खाएगा और आपको एक संकलन त्रुटि मिलेगी। IOW: Comparator.compareदो वस्तुओं की तुलना करने का अर्थ क्या है, इसके शब्दार्थ को पर्याप्त रूप से कैप्चर नहीं करता है, और इस प्रकार अन्य इंटरफेस जिनके पास गलती से वस्तुओं की तुलना करने के लिए बिल्कुल कुछ नहीं है, उसी प्रकार के हस्ताक्षर हैं।
जोर्ग डब्ल्यू मित्तग

19

यह काम करता है क्योंकि इंटरफ़ेस के Integer::minकार्यान्वयन को हल करता है Comparator<Integer>

Integer::minहल करने Integer.min(int a, int b), हल करने IntBinaryOperator, और संभवतः ऑटोबोक्सिंग के समाधान का तरीका संदर्भ कहीं न कहीं इसे बनाता है BinaryOperator<Integer>

और इंटरफ़ेस को लागू करने के लिए पूछें के min()सम्मान max()तरीके । अब यह एकल विधि का समाधान करता है । जो टाइप का हो ।Stream<Integer>Comparator<Integer>
Integer compareTo(Integer o1, Integer o2)BinaryOperator<Integer>

और इस प्रकार जादू हुआ है क्योंकि दोनों विधियां एक हैं BinaryOperator<Integer>


यह कहना पूरी तरह से सही नहीं है कि Integer::minलागू हो Comparable। यह एक प्रकार नहीं है जो कुछ भी लागू कर सकता है। लेकिन इसका मूल्यांकन उस वस्तु में किया जाता है जो लागू होती है Comparable
Lii

1
@Lii धन्यवाद, मैंने इसे अभी तय किया है।
skiwi

Comparator<Integer>एक एकल-सार-विधि (उर्फ "कार्यात्मक") इंटरफ़ेस है, और Integer::minइसके अनुबंध को पूरा करता है, इसलिए लैम्बडा की व्याख्या इस प्रकार की जा सकती है। मुझे नहीं पता कि आप बाइनरीऑपरेटर को यहां कैसे खेलते हुए देखते हैं (या इन्टिबिनरीऑपरेटर, या तो) - उस और कंपैक्टर के बीच कोई घटिया रिश्ता नहीं है।
पाओलो एबरमन

2

डेविड एम। लॉयड द्वारा दी गई जानकारी के अलावा, कोई भी ऐसा तंत्र जोड़ सकता है जो इसे अनुमति देता है जिसे टारगेट टाइपिंग कहा जाता है ।

यह विचार यह है कि कंपाइलर एक लैम्ब्डा एक्सप्रेशन के लिए असाइन होता है या एक मेथड रेफरेंस केवल एक्सप्रेशन पर ही निर्भर नहीं करता है, बल्कि यह भी है कि इसका उपयोग कहां किया जाता है।

एक अभिव्यक्ति का लक्ष्य वह चर है जिसके लिए उसका परिणाम असाइन किया गया है या जिस पैरामीटर से उसका परिणाम पारित हुआ है।

लैम्ब्डा अभिव्यक्तियों और विधि संदर्भों को एक प्रकार निर्दिष्ट किया जाता है जो उनके लक्ष्य के प्रकार से मेल खाता है, यदि इस प्रकार का एक प्रकार पाया जा सकता है।

देखें प्रकार निष्कर्ष अनुभाग में अधिक जानकारी के लिए जावा ट्यूटोरियल में।


1

मेरे पास एक त्रुटि थी जिसमें एक सरणी अधिकतम और न्यूनतम थी, इसलिए मेरा समाधान था:

int max = Arrays.stream(arrayWithInts).max().getAsInt();
int min = Arrays.stream(arrayWithInts).min().getAsInt();
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.