लैम्ब्डा एक्सप्रेशन के साथ स्ट्रीम () मैप (…) कैसे डिबग करें?


114

हमारी परियोजना में हम java 8 की ओर पलायन कर रहे हैं और हम इसकी नई विशेषताओं का परीक्षण कर रहे हैं।

अपने प्रोजेक्ट पर मैं अमरूद विधेय और कार्यों का उपयोग कर रहा है और फिल्टर होता का उपयोग कर कुछ संग्रह को बदलने के लिए Collections2.transformऔर Collections2.filter

इस माइग्रेशन पर मुझे उदाहरण के लिए अमरूद कोड को जावा 8 परिवर्तन के लिए बदलना होगा। इसलिए, मैं जो बदलाव कर रहा हूं वह इस प्रकार है:

List<Integer> naturals = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10,11,12,13);

Function <Integer, Integer> duplicate = new Function<Integer, Integer>(){
    @Override
    public Integer apply(Integer n)
    {
        return n * 2;
    }
};

Collection result = Collections2.transform(naturals, duplicate);

सेवा...

List<Integer> result2 = naturals.stream()
    .map(n -> n * 2)
    .collect(Collectors.toList());

अमरुद का उपयोग करते हुए मैं कोड को डिबग करने में बहुत असहज था क्योंकि मैं प्रत्येक परिवर्तन प्रक्रिया को डिबग कर सकता था लेकिन मेरी चिंता यह है कि उदाहरण के लिए डिबग कैसे किया जाए .map(n -> n*2)

डीबगर का उपयोग करके मैं कुछ कोड देख सकता हूं जैसे:

@Hidden
@DontInline
/** Interpretively invoke this form on the given arguments. */
Object interpretWithArguments(Object... argumentValues) throws Throwable {
    if (TRACE_INTERPRETER)
        return interpretWithArgumentsTracing(argumentValues);
    checkInvocationCounter();
    assert(arityCheck(argumentValues));
    Object[] values = Arrays.copyOf(argumentValues, names.length);
    for (int i = argumentValues.length; i < values.length; i++) {
        values[i] = interpretName(names[i], values);
    }
    return (result < 0) ? null : values[result];
}

लेकिन यह कोड को डिबग करने के लिए अमरूद जितना कठोर नहीं है, वास्तव में मुझे n * 2परिवर्तन नहीं मिला ।

क्या यह परिवर्तन देखने का कोई तरीका है या इस कोड को आसान डीबग करने का एक तरीका है?

संपादित करें: मैंने विभिन्न टिप्पणियों और पोस्ट किए गए उत्तरों से उत्तर जोड़ा है

Holgerमेरे सवाल का जवाब देने वाली टिप्पणी के लिए धन्यवाद , लैम्ब्डा ब्लॉक होने के दृष्टिकोण ने मुझे परिवर्तन प्रक्रिया को देखने की अनुमति दी और लैम्बडा शरीर के अंदर क्या हुआ, इस पर डिबग किया:

.map(
    n -> {
        Integer nr = n * 2;
        return nr;
    }
)

Stuart Marksविधि संदर्भ होने के दृष्टिकोण के लिए धन्यवाद भी मुझे परिवर्तन प्रक्रिया को डीबग करने की अनुमति देता है:

static int timesTwo(int n) {
    Integer result = n * 2;
    return result;
}
...
List<Integer> result2 = naturals.stream()
    .map(Java8Test::timesTwo)
    .collect(Collectors.toList());
...

Marlon Bernardesजवाब देने के लिए धन्यवाद मैंने देखा कि मेरा ग्रहण यह नहीं दिखाता है कि इसे क्या करना चाहिए और परिणाम प्रदर्शित करने के लिए झांकना () का उपयोग करने में मदद मिली।


आपको अपने अस्थायी resultचर को घोषित करने की आवश्यकता नहीं है Integer। एक साधारण के intरूप में अच्छी तरह से करना चाहिए यदि आप एक …mapintint
होल्गर

इसके अलावा, मैं मानता हूं कि IntelliJ IDEA 14. में बेहतर डिबगर है। अब हम लैमडा को डीबग कर सकते हैं।
मिखाइल

जवाबों:


86

मुझे आमतौर पर ग्रहण या IntelliJ IDEA का उपयोग करते समय मेमने के भावों को डिबग करने में कोई समस्या नहीं है। बस एक ब्रेकपॉइंट सेट करें और सुनिश्चित करें कि पूरी लैम्ब्डा अभिव्यक्ति का निरीक्षण न करें (केवल लैम्ब्डा बॉडी का निरीक्षण करें)।

लैम्ब्डा को डिबग करना

एक अन्य दृष्टिकोण peekधारा के तत्वों का निरीक्षण करने के लिए उपयोग करना है:

List<Integer> naturals = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12,13);
naturals.stream()
    .map(n -> n * 2)
    .peek(System.out::println)
    .collect(Collectors.toList());

अपडेट करें:

मुझे लगता है कि आप भ्रमित हो रहे हैं क्योंकि mapएक intermediate operation- दूसरे शब्दों में: यह एक आलसी ऑपरेशन है जिसे एक terminal operationनिष्पादित होने के बाद ही निष्पादित किया जाएगा । इसलिए जब आप stream.map(n -> n * 2)लैम्बडा बॉडी को कॉल करते हैं तो उस समय इसे निष्पादित नहीं किया जाता है। आपको एक टर्मिनल ऑपरेशन कहा जाता था ( collectइस मामले में) के बाद आपको एक ब्रेकपॉइंट सेट करने और उसका निरीक्षण करने की आवश्यकता है ।

आगे के स्पष्टीकरण के लिए स्ट्रीम ऑपरेशन की जाँच करें ।

अद्यतन 2:

होल्गर की टिप्पणी का उद्धरण :

यहाँ क्या मुश्किल है कि मैप और लैम्ब्डा एक्सप्रेशन को कॉल एक लाइन में है इसलिए एक लाइन ब्रेकप्वाइंट दो पूरी तरह से असंबंधित क्रियाओं पर रोक देगा।

एक लाइन ब्रेक map( लगाने के तुरंत बाद आप केवल लंबोदर अभिव्यक्ति के लिए एक ब्रेक प्वाइंट सेट करने की अनुमति देंगे। और यह असामान्य नहीं है कि डिबगर एक returnबयान के मध्यवर्ती मूल्यों को नहीं दिखाते हैं । मेमने को बदलने से n -> { int result=n * 2; return result; } आप परिणाम का निरीक्षण कर सकेंगे। फिर से, लाइन डालें जब लाइन द्वारा लाइन कदम…


प्रिंट स्क्रीन के लिए धन्यवाद। आपके पास ग्रहण का कौन सा संस्करण है या आपने उस संवाद को पाने के लिए क्या किया? मैं उपयोग करने की कोशिश inspectऔर display और मिल n cannot be resolved to a variable। Btw, झांकना भी उपयोगी है, लेकिन एक ही बार में सभी मूल्यों को प्रिंट करता है। मैं परिवर्तन की जाँच करने के लिए प्रत्येक पुनरावृत्ति प्रक्रिया को देखना चाहता हूँ। क्या यह पॉसिबल है?
फ़ेडरिको पियाज़ा

मैं ग्रहण केपलर SR2 (जावा के मार्केटप्लेस से स्थापित जावा 8 सपोर्ट के साथ) का उपयोग कर रहा हूं।
मार्लन बर्नार्डेस

क्या आप ग्रहण का भी उपयोग कर रहे हैं? बस .mapलाइन पर एक ब्रेकपॉइंट सेट करें और कई बार F8 दबाएं।
मार्लन बर्नार्ड्स

6
@ फ़ेड: यह यहाँ क्या मुश्किल बना देता है कि कॉल करने के लिए mapऔर लैम्ब्डा अभिव्यक्ति एक पंक्ति में हैं इसलिए एक लाइन ब्रेकप्वाइंट दो पूरी तरह से असंबंधित क्रियाओं पर रोक देगा। एक लाइन ब्रेक map(लगाने के तुरंत बाद आप केवल लंबोदर अभिव्यक्ति के लिए एक ब्रेक प्वाइंट सेट करने की अनुमति देंगे। और यह असामान्य नहीं है कि डिबगर एक returnबयान के मध्यवर्ती मूल्यों को नहीं दिखाते हैं । मेमने को बदलने से n -> { int result=n * 2; return result; }आप निरीक्षण कर सकते हैं result। फिर से, लाइन को लाइन से जोड़ते समय उचित रूप से लाइन ब्रेक
लगाएं

1
@ मार्लन बर्नार्ड्स: निश्चित रूप से, आप इसे उत्तर में जोड़ सकते हैं क्योंकि यह टिप्पणियों का उद्देश्य है: सामग्री को बेहतर बनाने में मदद करना। Btw।, मैंने कोड स्वरूपण में उद्धृत पाठ को संपादित किया है ...
Holger

33

IntelliJ में इस मामले के लिए जावा स्ट्रीम डीबगर प्लगइन के रूप में एक अच्छा प्लगइन है। आपको इसे देखना चाहिए: https://plugins.jetbrains.com/plugin/9696-java-stream-debugger?platform=hootsuite

यह ट्रेस करंट स्ट्रीम चेन बटन को जोड़कर IDEA डीबगर टूल विंडो को बढ़ाता है, जो स्ट्रीम एपीआई कॉल की एक श्रृंखला के अंदर डीबगर बंद होने पर सक्रिय हो जाता है।

इसमें अलग-अलग धाराओं के संचालन के साथ काम करने के लिए अच्छा इंटरफ़ेस है और आपको कुछ मूल्यों का पालन करने का अवसर प्रदान करता है जिन्हें यू को डिबग करना चाहिए।

जावा स्ट्रीम डीबगर

आप इसे मैन्युअल रूप से डिबग विंडो से यहां क्लिक करके लॉन्च कर सकते हैं:

यहां छवि विवरण दर्ज करें


23

नेटबैंस के साथ लैम्बडिंग भी अच्छी तरह से काम करती है। मैं NetBeans 8 और JDK 8u5 का उपयोग कर रहा हूं।

यदि आप एक लाइन पर एक ब्रेकपॉइंट सेट करते हैं जहां एक लंबोदर है, तो आप वास्तव में एक बार पाइप लाइन स्थापित होने पर हिट करेंगे, और फिर प्रत्येक स्ट्रीम तत्व के लिए। आपके उदाहरण का उपयोग करते हुए, पहली बार जब आप ब्रेकपॉइंट मारते हैं, तो वह map()कॉल होगी जो स्ट्रीम पाइपलाइन स्थापित कर रही है:

पहला ब्रेकपॉइंट

आप कॉल स्टैक और स्थानीय वैरिएबल और पैरामीटर मान देख सकते हैं जैसे mainकि आप अपेक्षा करते हैं। यदि आप आगे बढ़ना जारी रखते हैं, तो "वही" ब्रेकपॉइंट फिर से हिट हो जाता है, इस समय को छोड़कर यह लैम्बडा को कॉल के भीतर है:

यहां छवि विवरण दर्ज करें

ध्यान दें कि इस बार कॉल स्टैक मशीनरी के भीतर गहरी है, और स्थानीय चर लैम्ब्डा के स्थानीय हैं, न कि एनक्लोजिंग mainविधि। (मैंने इसे naturalsस्पष्ट करने के लिए सूची में मानों को बदल दिया है ।)

जैसा कि मार्लन बर्नार्डेस ने बताया (+1), आप peekमानों का निरीक्षण करने के लिए उपयोग कर सकते हैं क्योंकि वे पाइपलाइन में जाते हैं। हालांकि अगर आप इसे एक समानांतर धारा से उपयोग कर रहे हैं तो सावधान रहें। मूल्यों को विभिन्न थ्रेड्स में अप्रत्याशित क्रम में मुद्रित किया जा सकता है। यदि आप डिबगिंग डेटा संरचना से मान संग्रहीत कर रहे हैं peek, तो निश्चित रूप से डेटा संरचना को थ्रेड-सुरक्षित होना होगा।

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

static int timesTwo(int n) {
    return n * 2;
}

public static void main(String[] args) {
    List<Integer> naturals = Arrays.asList(3247,92837,123);
    List<Integer> result =
        naturals.stream()
            .map(DebugLambda::timesTwo)
            .collect(toList());
}

इससे यह देखना आसान हो सकता है कि आप डिबगिंग करते समय क्या हो रहा है। इसके अलावा, इस तरीके को निकालने से यूनिट टेस्ट करना आसान हो जाता है। यदि आपका लैम्ब्डा इतना जटिल है कि आपको इसके माध्यम से सिंगल-स्टेपिंग करने की आवश्यकता है, तो आप शायद वैसे भी इसके लिए यूनिट परीक्षणों का एक गुच्छा चाहते हैं।


मेरी समस्या यह थी कि मैं मेमने के शरीर को डिबग नहीं कर सकता था, लेकिन विधि संदर्भों का उपयोग करने के आपके दृष्टिकोण से मुझे बहुत मदद मिली जो मुझे चाहिए था। आप होल्गर दृष्टिकोण का उपयोग करके अपने उत्तर को अपडेट कर सकते हैं { int result=n * 2; return result; }जो विभिन्न लाइनों में जोड़कर पूरी तरह से काम करता है और मैं उत्तर को स्वीकार कर सकता हूं क्योंकि दोनों उत्तर सहायक थे। निश्चित रूप से +1।
फेडरिको पियाज़ा

1
@ फ़ेड लगता है कि अन्य उत्तर पहले से ही अपडेट हो गए हैं, इसलिए मुझे अपडेट करने की कोई आवश्यकता नहीं है। मुझे वैसे भी मल्टी-लाइन लैम्ब्डा से नफरत है। :-)
स्टुअर्ट मार्क्स

1
@ स्टुअर्ट मार्क्स: मुझे सिंगल लाइन लैम्बडा भी पसंद है। इसलिए आमतौर पर मैं डिबगिंग के बाद लाइन ब्रेक को हटा देता हूं जो अन्य (साधारण) यौगिक कथनों पर लागू होता है।
होल्गर

1
@ फ़ेड कोई चिंता नहीं। यह पूछने वाले के रूप में आपका विशेषाधिकार है कि आप जो भी पसंद करते हैं, उसे स्वीकार करें। +1 के लिए धन्यवाद।
स्टुअर्ट मार्क्स

1
मुझे लगता है कि यूनिट के परीक्षणों के तरीकों को आसान बनाने के अलावा, विधि संदर्भ बनाना भी अधिक पठनीय कोड के लिए बनाता है। बहुत बढ़िया जवाब! (+1)
मार्लन बर्नार्ड्स


6

बस अधिक अद्यतन विवरण (अक्टूबर 2019) प्रदान करने के लिए, IntelliJ ने इस प्रकार के कोड को डीबग करने के लिए एक बहुत अच्छा एकीकरण जोड़ा है जो बेहद उपयोगी है।

जब हम एक लाइन पर रुकते हैं जिसमें एक लंबोदर होता है अगर हम दबाते हैं F7(कदम में) तो इंटेलीजे हाइलाइट करेगा कि स्निपेट को डीबग करने के लिए क्या होगा। हम डिसकस कर सकते हैं कि किस चांस को डिबग करना है Tabऔर एक बार हमने तय कर लिया तो हम F7फिर से क्लिक करते हैं।

यहाँ कुछ स्क्रीनशॉट को स्पष्ट करने के लिए:

1- प्रेस F7(कदम में) कुंजी, हाइलाइट्स (या चयन मोड) प्रदर्शित करेगा यहां छवि विवरण दर्ज करें

2- Tabस्निपेट का चयन डिबग करने के लिए कई बार करें यहां छवि विवरण दर्ज करें

3- प्रेस F7(कदम में) कुंजी में कदम है यहां छवि विवरण दर्ज करें


1

IDE का उपयोग करने वाले डिबगिंग हमेशा सहायक होते हैं, लेकिन एक स्ट्रीम विधि में प्रत्येक तत्व के माध्यम से डिबगिंग का आदर्श तरीका टर्मिनल विधि के संचालन से पहले () का उपयोग होता है क्योंकि जावा स्टीम्स का आलसी मूल्यांकन किया जाता है, इसलिए जब तक कि टर्मिनल विधि लागू नहीं होती है, संबंधित स्ट्रीम होगा मूल्यांकन नहीं किया जाना चाहिए।

List<Integer> numFromZeroToTen = Arrays.asList(1,2,3,4,5,6,7,8,9,10);

    numFromZeroToTen.stream()
        .map(n -> n * 2)
        .peek(n -> System.out.println(n))
        .collect(Collectors.toList());
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.