कैसे एक विधि संदर्भ की उपेक्षा करने के लिए विधेय


330

जावा 8 में, आप एक स्ट्रीम को फ़िल्टर करने के लिए एक विधि संदर्भ का उपयोग कर सकते हैं, उदाहरण के लिए:

Stream<String> s = ...;
long emptyStrings = s.filter(String::isEmpty).count();

क्या एक विधि संदर्भ बनाने का एक तरीका है जो किसी मौजूदा की उपेक्षा है, जैसे कि:

long nonEmptyStrings = s.filter(not(String::isEmpty)).count();

मैं notनीचे की तरह विधि बना सकता हूं लेकिन मैं सोच रहा था कि क्या जेडीके ने कुछ इसी तरह की पेशकश की थी।

static <T> Predicate<T> not(Predicate<T> p) { return o -> !p.test(o); }

6
JDK-8050818 एक स्थिर Predicate.not(Predicate)विधि के अतिरिक्त को शामिल करता है । लेकिन यह मुद्दा अभी भी खुला है इसलिए हम इसे जावा 12 (यदि कभी हो) में जल्द से जल्द देखेंगे।
स्टीफन ज़ॉबेल

1
इस उत्तर की तरह लगता है JDK / 11 में भी अंतिम समाधान अनुकूलित किया जा सकता है।
नमन

2
मैं वास्तव में इस मामले के लिए एक विशेष विधि संदर्भ वाक्यविन्यास देखना चाहूंगा: s.filter (स्ट्रिंग ::! Ismmpty)
माइक ट्वेन

जवाबों:



214

मैं इनलाइन उपयोग किए जाने वाले विधि संदर्भ के लिए अनुमति देने के लिए निम्नलिखित को स्थिर आयात करने की योजना बना रहा हूं:

public static <T> Predicate<T> not(Predicate<T> t) {
    return t.negate();
}

जैसे

Stream<String> s = ...;
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();

अद्यतन : जावा -11 से शुरू होकर, JDK एक समान समाधान भी उपलब्ध कराता है।


9
@ सेंटहिल लेकिन फिर आपको इसे लिखना होगा, पैरामीटर को एक नाम देना होगा
फ्लॉप



149

एक विधि संदर्भ की रचना करने का एक तरीका है जो एक वर्तमान विधि संदर्भ के विपरीत है। नीचे @ vlasec का उत्तर देखें जो दिखाता है कि कैसे विधि संदर्भ को स्पष्ट रूप से कास्टिंग करके Predicateऔर फिर negateफ़ंक्शन का उपयोग करके इसे परिवर्तित करना । यही कारण है कि कुछ अन्य तरीकों में से एक तरीका यह करने के लिए बहुत परेशानी भरा नहीं है।

इस के विपरीत:

Stream<String> s = ...;
int emptyStrings = s.filter(String::isEmpty).count();

क्या यह:

Stream<String> s = ...;
int notEmptyStrings = s.filter(((Predicate<String>) String::isEmpty).negate()).count()

या यह:

Stream<String> s = ...;
int notEmptyStrings = s.filter( it -> !it.isEmpty() ).count();

व्यक्तिगत रूप से, मैं बाद की तकनीक को पसंद करता हूं क्योंकि मुझे it -> !it.isEmpty()एक लंबी क्रिया स्पष्ट कलाकारों की तुलना में पढ़ने के लिए स्पष्ट लगता है और फिर नकारात्मक।

एक भी एक विधेय बना सकता है और उसका पुन: उपयोग कर सकता है:

Predicate<String> notEmpty = (String it) -> !it.isEmpty();

Stream<String> s = ...;
int notEmptyStrings = s.filter(notEmpty).count();

या, यदि कोई संग्रह या सरणी है, तो बस एक लूप का उपयोग करें जो सरल है, कम ओवरहेड है, और * तेजी से ** हो सकता है:

int notEmpty = 0;
for(String s : list) if(!s.isEmpty()) notEmpty++;

* यदि आप जानना चाहते हैं कि क्या तेज है, तो JMH http://openjdk.java.net/projects/code-tools/jmh का उपयोग करें , और जब तक यह सभी JVM ऑप्टिमाइज़ेशन से बच नहीं जाता है तब तक हाथ बेंचमार्क कोड से बचें। जावा 8: स्ट्रीम का प्रदर्शन बनाम संग्रह

** मुझे यह सुझाव देने के लिए फ्लैक मिल रहा है कि लूप तकनीक तेज है। यह एक धारा निर्माण को समाप्त करता है, यह एक अन्य विधि कॉल (विधेय के लिए नकारात्मक कार्य) का उपयोग करके समाप्त करता है, और यह एक अस्थायी संचायक सूची / काउंटर को समाप्त करता है। तो कुछ चीजें जो अंतिम निर्माण द्वारा बचाई जाती हैं जो इसे तेज कर सकती हैं।

मुझे लगता है कि यह सरल और अच्छा है, भले ही यह तेज न हो। यदि नौकरी एक हथौड़ा और एक कील के लिए बुलाती है, तो एक जंजीर और गोंद में मत लाओ! मुझे पता है कि आप में से कुछ इसके साथ समस्या लेते हैं।

विश-लिस्ट: मैं देखना चाहूंगा कि जावा Streamफ़ंक्शंस अब थोड़ा विकसित हो रहे हैं कि जावा उपयोगकर्ता उनसे अधिक परिचित हैं। उदाहरण के लिए, स्ट्रीम में 'गणना' विधि को स्वीकार Predicateकर सकता है ताकि यह सीधे इस तरह किया जा सके:

Stream<String> s = ...;
int notEmptyStrings = s.count(it -> !it.isEmpty());

or

List<String> list = ...;
int notEmptyStrings = lists.count(it -> !it.isEmpty());

आप यह क्यों कहते हैं कि यह बहुत तेज़ है ?
जोस एंडियास

@ JoséAndias (1) क्या यह तेज है या 'बहुत तेज है'? (२) यदि ऐसा है तो क्यों? आपने क्या निर्धारित किया है?
समन्वयक

3
मैं आपसे "दौड़ने के लिए बहुत तेज" पर विस्तार से पूछ रहा हूं। प्रश्न: (1) क्या यह तेज है या 'बहुत तेज है'? (२) यदि ऐसा है तो क्यों? आपने क्या निर्धारित किया है? आपके द्वारा बेहतर उत्तर दिया जाता है, बयान के लेखक। मैं इसे तेज या धीमा नहीं मान रहा हूं। धन्यवाद
जोस Andias

2
फिर मैं इसे आपके विचार के लिए फेंक दूंगा - यह एक स्ट्रीम निर्माण को समाप्त करता है, यह एक अन्य विधि कॉल (विधेय के लिए नकारात्मक कार्य) का उपयोग करके समाप्त करता है, और यह एक अस्थायी संचायक सूची / काउंटर को समाप्त करता है। तो कुछ चीजें जो अंतिम निर्माण द्वारा बचाई जाती हैं। मुझे यकीन नहीं है कि यह तेज है या कितना तेज है, लेकिन मुझे लगता है कि यह बहुत तेज है। लेकिन शायद 'बहुत कुछ' व्यक्तिपरक है। एक नकारात्मक गिनती करने और एक सीधी गिनती करने के लिए धाराओं की तुलना में बाद में कोड करना सरल है। मेरी प्राथिमिकता ।
समन्वयक

4
negate () एक आदर्श समाधान की तरह लगता है। अफ़सोस की बात है Predicate.negate(String::isEmpty);कि बोझिल कास्टिंग के बिना यह स्थिर नहीं है ।
जोएल शेम्तोव

91

Predicateतरीके हैं and, orऔर negate

हालाँकि, String::isEmptyयह एक नहीं है Predicate, यह सिर्फ एक String -> Booleanमेमना है और यह अभी भी कुछ भी बन सकता है, उदा Function<String, Boolean>टाइप इंट्रेंस वही है जो पहले होना चाहिए। filterविधि infers टाइप परोक्ष । लेकिन अगर आप इसे तर्क के रूप में पारित करने से पहले इसे नकार देते हैं, तो यह अब नहीं होता है। जैसा कि @axtavt ने उल्लेख किया है, स्पष्ट अनुमान का इस्तेमाल बदसूरत तरीके से किया जा सकता है:

s.filter(((Predicate<String>) String::isEmpty).negate()).count()

अन्य उत्तरों में अन्य तरीके सुझाए गए हैं, जिनमें स्टैटिक notविधि और लैम्ब्डा के साथ सबसे अच्छे विचार होने की संभावना है। यह tl; डॉ । सेक्शन का समापन करता है ।


हालाँकि, यदि आप लैम्ब्डा प्रकार के निष्कर्ष की कुछ गहरी समझ चाहते हैं, तो मैं उदाहरणों का उपयोग करके इसे थोड़ा और गहराई से समझाना चाहता हूँ। इन्हें देखें और जानने की कोशिश करें कि क्या होता है:

Object obj1                  = String::isEmpty;
Predicate<String> p1         = s -> s.isEmpty();
Function<String, Boolean> f1 = String::isEmpty;
Object obj2                  = p1;
Function<String, Boolean> f2 = (Function<String, Boolean>) obj2;
Function<String, Boolean> f3 = p1::test;
Predicate<Integer> p2        = s -> s.isEmpty();
Predicate<Integer> p3        = String::isEmpty;
  • obj1 संकलन नहीं करता है - लंबोदर को एक कार्यात्मक इंटरफ़ेस (= एक सार पद्धति के साथ) की आवश्यकता है
  • पी 1 और एफ 1 ठीक काम करते हैं, प्रत्येक एक अलग प्रकार का संदर्भ दे रहा है
  • obj2 एक Predicateको Objectमूर्खतापूर्ण लेकिन मान्य बनाता है
  • f2 कार्यावधि में विफल रहता है - आप डाली नहीं कर सकते Predicateकरने के लिए Function, अब या निष्कर्ष के बारे में
  • f3 काम करता है - आप विधेय विधि को कहते हैं जो testइसके लंबो द्वारा परिभाषित होती है
  • P2 संकलन नहीं करता है - विधि Integerनहीं हैisEmpty
  • p3 संकलन नहीं करता है - तर्क के String::isEmptyसाथ कोई स्थिर विधि नहीं Integerहै

मुझे आशा है कि यह कुछ और अंतर्दृष्टि प्राप्त करने में मदद करता है कि किस प्रकार से बांझपन काम करता है।


45

अन्य के उत्तर और व्यक्तिगत अनुभव पर निर्माण:

Predicate<String> blank = String::isEmpty;
content.stream()
       .filter(blank.negate())

4
दिलचस्प है - आप कार्यात्मक ::संदर्भ को इनलाइन नहीं कर सकते हैं, जैसे कोई भी इच्छा कर सकता है ( String::isEmpty.negate()), लेकिन यदि आप पहले एक चर (या Predicate<String>पहले डाली ) पर काम करते हैं, तो वह काम करता है। मुझे लगता है कि लैम्ब्डा w / !ज्यादातर मामलों में सबसे अधिक पठनीय होगा, लेकिन यह जानना उपयोगी है कि क्या कर सकते हैं और क्या संकलन नहीं कर सकते।
जोशुआ गोल्डबर्ग

2
@ जोशुआगोल्डबर्ग मैंने अपने उत्तर में बताया कि विधि संदर्भ अपने आप में एक विधेय नहीं है। यहां, कास्टिंग चर द्वारा किया जाता है।
वाल्सेक

17

एक और विकल्प गैर-अस्पष्ट संदर्भों में लैम्ब्डा कास्टिंग का एक वर्ग में उपयोग करना है:

public static class Lambdas {
    public static <T> Predicate<T> as(Predicate<T> predicate){
        return predicate;
    }

    public static <T> Consumer<T> as(Consumer<T> consumer){
        return consumer;
    }

    public static <T> Supplier<T> as(Supplier<T> supplier){
        return supplier;
    }

    public static <T, R> Function<T, R> as(Function<T, R> function){
        return function;
    }

}

... और फिर स्थैतिक उपयोगिता वर्ग का आयात करें:

stream.filter(as(String::isEmpty).negate())

1
मैं वास्तव में इस काम को आश्चर्यचकित करता हूं - लेकिन ऐसा लगता है कि JDK फंक्शन <T> ओवर, बूलियन> पर Predicate करता है। लेकिन आपको फंक्शन <टी, बुलियन> में कुछ भी डालने के लिए लैम्ब्डा नहीं मिलेगा।
वलसेक

यह स्ट्रिंग के लिए काम करता है लेकिन सूची के लिए नहीं: त्रुटि: (20, 39) जावा: के रूप में संदर्भ अस्पष्ट दोनों विधि <t> के रूप में है (com.strands.sbs.function में java.util.function.Consumer <T>)। लैंबडास और विधि <T, R> as (java.util.function.Function <T, R>) com.strands.sbs.function.Lambdas मैच में
डैनियल

डैनियल, यह हो सकता है यदि आप ओवरलोड विधि का उपयोग करने की कोशिश कर रहे हैं :)
आस्कर कल्यकोव

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

12

Predicate#negateक्या आप के लिए देख रहे हो नहीं होना चाहिए ?


आपको Predicateपहले प्राप्त करने की आवश्यकता है ।
सोतीरियोस डेलिमोलिस

21
आपको पहले कास्ट String::isEmpty()करना होगा Predicate<String>- यह बहुत बदसूरत है।
axtavt

3
@assylias उपयोग के रूप में Predicate<String> p = (Predicate<String>) String::isEmpty;और p.negate()
सोतिरियोस डेलिमोलिस

8
@SotiriosDelimanolis मुझे पता है, लेकिन यह उद्देश्य को हरा देता है - मैं s -> !s.isEmpty()उस मामले में लिखूंगा !
assylias

@ कसीलियास: हाँ, मेरा मानना ​​है कि वास्तव में यह विचार है; कि सिर्फ लंबोदर लोंगहैंड लिखना ही अभीष्ट पतन है।
लुई वासरमैन

8

इस मामले में यू का उपयोग कर सकते हैं org.apache.commons.lang3.StringUtilsऔर करते हैं

int nonEmptyStrings = s.filter(StringUtils::isNotEmpty).count();

6
नहीं। सवाल यह है कि किसी भी विधि संदर्भ को कैसे नकारा जाए, और String::isEmptyउदाहरण के रूप में लिया जाए। यह अभी भी प्रासंगिक जानकारी है यदि आपके पास यह उपयोग मामला है, लेकिन यदि यह केवल स्ट्रिंग उपयोग मामले का जवाब देता है, तो इसे स्वीकार नहीं किया जाना चाहिए।
एंथनी ड्रॉगन

4

मैंने एक पूर्ण उपयोगिता वर्ग (अस्कर के प्रस्ताव से प्रेरित) लिखा है जो जावा 8 लैम्ब्डा अभिव्यक्ति को ले सकता है और उन्हें (यदि लागू हो) पैकेज में परिभाषित किसी भी टाइप किए गए मानक जावा 8 लैम्ब्डा में बदल सकता है java.util.function। आप उदाहरण के लिए कर सकते हैं:

  • asPredicate(String::isEmpty).negate()
  • asBiPredicate(String::equals).negate()

क्योंकि वहाँ कई अस्पष्टता होगी यदि सभी स्थिर तरीकों को केवल नाम दिया जाएगा as(), तो मैंने विधि को "के रूप में" वापस करने का विकल्प चुना। यह हमें लंबोदर व्याख्या का पूर्ण नियंत्रण देता है। नीचे इस्तेमाल किए गए पैटर्न का खुलासा करते हुए (कुछ बड़े) उपयोगिता वर्ग का पहला भाग है।

पर एक नज़र डालें यहाँ पूरा वर्ग (सार पर)।

public class FunctionCastUtil {

    public static <T, U> BiConsumer<T, U> asBiConsumer(BiConsumer<T, U> biConsumer) {
        return biConsumer;
    }

    public static <T, U, R> BiFunction<T, U, R> asBiFunction(BiFunction<T, U, R> biFunction) {
        return biFunction;
    }

     public static <T> BinaryOperator<T> asBinaryOperator(BinaryOperator<T> binaryOperator) {
        return binaryOperator;
    }

    ... and so on...
}

4

आप एक्लिप्स कलेक्शंस से प्रेडिकेट्स का उपयोग कर सकते हैं

MutableList<String> strings = Lists.mutable.empty();
int nonEmptyStrings = strings.count(Predicates.not(String::isEmpty));

यदि आप स्ट्रिंग को नहीं बदल सकते हैं List:

List<String> strings = new ArrayList<>();
int nonEmptyStrings = ListAdapter.adapt(strings).count(Predicates.not(String::isEmpty));

यदि आपको केवल एक नकार की आवश्यकता है String.isEmpty()तो आप भी इसका उपयोग कर सकते हैं StringPredicates.notEmpty()

नोट: मैं कलेक्शन एक्लिप्स का योगदानकर्ता हूं।



0

यदि आप स्प्रिंग बूट (2.0.0+) का उपयोग कर रहे हैं, तो आप इसका उपयोग कर सकते हैं:

import org.springframework.util.StringUtils;

...
.filter(StringUtils::hasLength)
...

जो करता है: return (str != null && !str.isEmpty());

तो यह के लिए आवश्यक नकारात्मक प्रभाव पड़ेगा isEmpty

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