एक लाइनर में स्ट्रीम / सूची का अंतिम तत्व प्राप्त करें


118

मैं निम्नलिखित कोड में एक स्ट्रीम या सूची का अंतिम तत्व कैसे प्राप्त कर सकता हूं?

कहां data.careasहै List<CArea>:

CArea first = data.careas.stream()
                  .filter(c -> c.bbox.orientationHorizontal).findFirst().get();

CArea last = data.careas.stream()
                 .filter(c -> c.bbox.orientationHorizontal)
                 .collect(Collectors.toList()).; //how to?

जैसा कि आप देख सकते हैं पहला तत्व, एक निश्चित के साथ filter, कठिन नहीं है।

हालांकि एक लाइनर में अंतिम तत्व प्राप्त करना एक वास्तविक दर्द है:

  • ऐसा लगता है कि मैं इसे सीधे से प्राप्त नहीं कर सकता Stream। (यह केवल परिमित धाराओं के लिए समझ में आएगा)
  • ऐसा भी लगता है कि आप इंटरफेस से first()और जैसी चीजें प्राप्त नहीं कर सकते , जो वास्तव में एक दर्द है।last()List

मुझे इंटरफ़ेस में first()और last()विधि प्रदान नहीं करने के लिए कोई तर्क नहीं दिखता Listहै, क्योंकि वहां के तत्वों का आदेश दिया गया है, और इसके अलावा आकार ज्ञात है।

लेकिन मूल उत्तर के अनुसार: परिमित के अंतिम तत्व को कैसे प्राप्त करें Stream?

व्यक्तिगत रूप से, यह सबसे निकटतम है जो मुझे मिल सकता है:

int lastIndex = data.careas.stream()
        .filter(c -> c.bbox.orientationHorizontal)
        .mapToInt(c -> data.careas.indexOf(c)).max().getAsInt();
CArea last = data.careas.get(lastIndex);

हालांकि, इसमें indexOfहर तत्व का उपयोग करना शामिल है, जो कि आम तौर पर आप नहीं चाहते हैं क्योंकि यह प्रदर्शन को ख़राब कर सकता है।


10
अमरूद प्रदान करता है Iterables.getLastजो Iterable लेता है लेकिन साथ काम करने के लिए अनुकूलित है List। एक पालतू पशु है कि यह नहीं है getFirstStreamसामान्य रूप में एपीआई सुविधा तरीकों की बहुत सारी छोड़ते हुए बुरी तरह गुदा है। कॉन्स्ट्रास्ट द्वारा C # का LINQ, प्रदान करने के लिए खुश है .Last()और यहां तक ​​कि .Last(Func<T,Boolean> predicate), यह अनंत Enumerables का भी समर्थन करता है।
हांग्जो डबिन्स्की

@AleksandrDubinsky अपवित्र, लेकिन पाठकों के लिए एक नोट। Streamएपीआई पूरी तरह से तुलनीय नहीं है LINQक्योंकि दोनों एक बहुत ही अलग प्रतिमान में किए गए हैं। यह बदतर या बेहतर नहीं है यह सिर्फ अलग है। और निश्चित रूप से कुछ विधियां अनुपस्थित हैं क्योंकि
ऑर्कल देव

1
एक सच्चे वन-लाइनर के लिए, यह धागा उपयोग का हो सकता है।
क्वांटम

जवाबों:


185

विधि के साथ अंतिम तत्व प्राप्त करना संभव है स्ट्रीम :: कम करें । निम्नलिखित सूची में सामान्य मामले के लिए एक न्यूनतम उदाहरण है:

Stream<T> stream = ...; // sequential or parallel stream
Optional<T> last = stream.reduce((first, second) -> second);

यह कार्यान्वयन सभी ऑर्डर की गई धाराओं ( सूचियों से बनाई गई धाराओं सहित ) के लिए काम करता है । के लिए बिना क्रम वाली धाराओं यह अनिर्दिष्ट स्पष्ट कारणों के जो तत्व लौटा दी जाएगी के लिए है।

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

  • विधि स्ट्रीम के लिए Javadoc :: राज्यों को कम करता है, कि यह " क्रमिक रूप से निष्पादित करने के लिए विवश नहीं है "
  • Javadoc को यह भी आवश्यकता है कि "संचायक फ़ंक्शन को दो मानों के संयोजन के लिए एक साहचर्य , गैर-हस्तक्षेपकारी , स्टेटलेस फ़ंक्शन होना चाहिए " , जो स्पष्ट रूप से लैम्ब्डा अभिव्यक्ति के लिए मामला है (first, second) -> second
  • कटौती के संचालन के लिए Javadoc में कहा गया है: "धाराओं वर्गों में सामान्य कमी संचालन के कई रूप होते हैं, जिन्हें कम () और इकट्ठा ( []] कहा जाता है और " एक उचित रूप से निर्मित कम करने वाला ऑपरेशन स्वाभाविक रूप से समानांतर होता है , इसलिए फ़ंक्शन के रूप में लंबे समय तक। ) तत्वों को संसाधित करने के लिए उपयोग किया जाता है जो साहचर्य और स्टेटलेस हैं । "

निकटता से संबंधित कलेक्टरों के लिए प्रलेखन और भी अधिक स्पष्ट है: "यह सुनिश्चित करने के लिए कि अनुक्रमिक और समानांतर निष्पादन बराबर परिणाम उत्पन्न करते हैं , कलेक्टर के कार्यों को एक पहचान और एक संबद्धता बाधाओं को पूरा करना होगा ।"


मूल प्रश्न पर वापस जाएं: निम्न कोड चर में अंतिम तत्व के लिए एक संदर्भ संग्रहीत करता है lastऔर धारा खाली होने पर अपवाद को फेंकता है। जटिलता धारा की लंबाई में रैखिक है।

CArea last = data.careas
                 .stream()
                 .filter(c -> c.bbox.orientationHorizontal)
                 .reduce((first, second) -> second).get();

अच्छा लगा, धन्यवाद! क्या आप जानते हैं कि क्या यह संभव है कि _ऐसे मामलों में जहां आपको पैरामीटर की आवश्यकता नहीं है, एक नाम (शायद या इसी तरह का उपयोग करके ) को छोड़ दें ? तो यह होगा: .reduce((_, current) -> current)यदि केवल वही सिंटैक्स मान्य है।
skivi

2
@skiwi आप किसी भी कानूनी चर नाम का उपयोग कर सकते हैं, उदाहरण के लिए: .reduce(($, current) -> current)या .reduce((__, current) -> current)(डबल अंडरस्कोर)।
10

2
तकनीकी रूप से, यह किसी भी स्ट्रीम के लिए काम नहीं कर सकता है। जिस दस्तावेज़ को आप इंगित करते हैं, साथ ही साथ Stream.reduce(BinaryOperator<T>)कोई भी उल्लेख नहीं reduceकरता है यदि मुठभेड़ आदेश का पालन करता है, और एक टर्मिनल ऑपरेशन मुठभेड़ आदेश की अनदेखी करने के लिए स्वतंत्र है, भले ही धारा का आदेश दिया गया हो। एक तरफ के रूप में, शब्द "कम्यूटेटिव" स्ट्रीम javadocs में दिखाई नहीं देता है, इसलिए इसकी अनुपस्थिति हमें बहुत कुछ नहीं बताती है।
हांग्जो डबलिनस्की

2
@AleksandrDubinsky: वास्तव में, प्रलेखन कम्यूटेटिव का उल्लेख नहीं करता है , क्योंकि यह कम ऑपरेशन के लिए प्रासंगिक नहीं है । महत्वपूर्ण हिस्सा यह है: "[..] एक ठीक से निर्मित कम करने का ऑपरेशन स्वाभाविक रूप से समानांतर है, इसलिए जब तक तत्वों को संसाधित करने के लिए उपयोग किए जाने वाले फ़ंक्शन (एस) सहयोगी होते हैं [..]।"
nosid

2
@ एलेक्ज़ेंडर डबिन्स्की: बेशक, यह "युक्ति का सैद्धांतिक सवाल" नहीं है। यह reduce((a,b)->b)अंतिम तत्व (एक आदेशित धारा, निश्चित रूप से) या नहीं पाने के लिए एक सही समाधान होने के बीच अंतर करता है । ब्रायन गोएट्ज़ का कथन एक बिंदु बनाता है, आगे एपीआई प्रलेखन बताता है कि reduce("", String::concat)स्ट्रिंग संघनन के लिए एक अक्षम लेकिन सही समाधान है, जो मुठभेड़ आदेश के रखरखाव का मतलब है। इरादा अच्छी तरह से ज्ञात है, प्रलेखन को पकड़ना है।
होल्गर

42

यदि आपके पास संग्रह (या अधिक सामान्य Iterable) है तो आप Google अमरूद का उपयोग कर सकते हैं

Iterables.getLast(myIterable)

ऑनलाइनर के रूप में।


1
और आप आसानी से एक धारा को पुनरावृत्त में बदल सकते हैं:Iterables.getLast(() -> data.careas.stream().filter(c -> c.bbox.orientationHorizontal).iterator())
shmosel

10

एक लाइनर (स्ट्रीम के लिए कोई ज़रूरत नहीं है;):

Object lastElement = list.get(list.size()-1);

30
यदि सूची खाली है, तो यह कोड फेंक देगा ArrayIndexOutOfBoundsException
ड्रैगन

8

इस मामले के लिए अमरूद समर्पित है:

Stream<T> stream = ...;
Optional<T> lastItem = Streams.findLast(stream);

यह बराबर है stream.reduce((a, b) -> b)लेकिन रचनाकारों का दावा है कि इसमें बहुत बेहतर प्रदर्शन है।

से प्रलेखन :

इस पद्धति का रनटाइम ओ (लॉग एन) और ओ (एन) के बीच होगा, जो कुशलता से प्रभावी स्ट्रीम पर बेहतर प्रदर्शन करेगा।

यह उल्लेख के लायक है कि यदि धारा अनियंत्रित है तो यह विधि जैसा व्यवहार करती है findAny()


1
एक तरह से ... होल्गर के @ZhekaKozlov इसके साथ कुछ खामियों से पता चला है यहाँ
यूजीन

0

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

    final Queue<Integer> queue = new LinkedList<>();
    final int N=5;
    list.stream().peek((z) -> {
        queue.offer(z);
        if (queue.size() > N)
            queue.poll();
    }).count();

एक अन्य विकल्प कतार के रूप में पहचान का उपयोग करके कम करने वाले ऑपरेशन का उपयोग करना हो सकता है।

    final int lastN=3;
    Queue<Integer> reduce1 = list.stream()
    .reduce( 
        (Queue<Integer>)new LinkedList<Integer>(), 
        (m, n) -> {
            m.offer(n);
            if (m.size() > lastN)
               m.poll();
            return m;
    }, (m, n) -> m);

    System.out.println("reduce1 = " + reduce1);

-1

आप नीचे के रूप में स्किप () फ़ंक्शन का भी उपयोग कर सकते हैं ...

long count = data.careas.count();
CArea last = data.careas.stream().skip(count - 1).findFirst().get();

यह उपयोग करने के लिए सुपर सरल है।


ध्यान दें: विशाल संग्रह (लाखों प्रविष्टियों) के साथ काम करते समय आपको स्ट्रीम के "स्किप" पर भरोसा नहीं करना चाहिए, क्योंकि "स्किप" को एनएचटी नंबर तक पहुंचने तक सभी तत्वों के माध्यम से पुनरावृत्ति करके लागू किया जाता है। इसे आजमाया। साधारण गेट-टू-इंडेक्स ऑपरेशन की तुलना में प्रदर्शन से बहुत निराश था।
java.is.for.desktop

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