2015-04-12 11 views
8

मैं गैर-टर्मिनल ग्रुपिंग ऑपरेशन को लागू करने का एक तरीका ढूंढ रहा हूं, जैसे मेमोरी ओवरहेड न्यूनतम होगा।जावा स्ट्रीम - क्रमबद्ध धाराओं पर क्रमबद्ध वस्तुओं को

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

मुझे पता है कि मैं इसे इटरेटर रैपर का उपयोग करके और समूहबद्ध तर्क को लागू करने के लिए इसे प्राप्त कर सकता हूं। इसके बजाए स्ट्रीम एपीआई का उपयोग करके इसे कार्यान्वित करने का कोई आसान तरीका है?

private static class DedupSeq implements IntFunction<IntStream> { 
    private Integer prev; 

    @Override 
    public IntStream apply(int value) { 
     IntStream res = (prev != null && value == prev)? IntStream.empty() : IntStream.of(value); 
     prev = value; 
     return res; 
    }  
    } 

और फिर:

- - संपादित

मैं (..) इस लक्ष्य को हासिल करने के लिए एक तरह से Stream.flatMap दुरुपयोग पाया

IntStream.of(1,1,3,3,3,4,4,5).flatMap(new DedupSeq()).forEach(System.out::println); 

कौन सा प्रिंट:

1 
3 
4 
5 

कुछ बदलावों के साथ, उसी तकनीक का उपयोग धाराओं के किसी भी प्रकार के स्मृति-कुशल अनुक्रम समूह के लिए किया जा सकता है। वैसे भी, मुझे इस समाधान को बहुत पसंद नहीं है, और मैं कुछ और प्राकृतिक खोज रहा था (उदाहरण के लिए मैपिंग या फ़िल्टरिंग के तरीके की तरह)। इसके अलावा, मैं यहां अनुबंध तोड़ रहा हूं क्योंकि flatMap (..) को प्रदान किया गया कार्य राज्यपूर्ण है।

+2

आप हमेशा '.filter (someSet :: जोड़ें) ', लेकिन क्या आपने सादे 'विशिष्ट()' के साथ इस तरह के समाधान के प्रदर्शन की कोशिश की और तुलना की है? साथ ही, आप "सामान्य मामले में" कहते हैं, लेकिन हो सकता है कि 'स्ट्रीम' _is_ 'ORDERED', ठीक से (या अधिक सटीक रूप से, इसके अंतर्निहित 'स्प्लिटरेटर') – fge

+0

@fge: मुझे यकीन नहीं है कि वहां कोई अनुकूलन है। कोड: IntStream.range (0, 100000000) .डिस्टिंक()। प्रत्येक (x -> {}) के लिए; अंतर्निहित स्प्लिटरेटर स्वयं को ऑर्डर करने की रिपोर्ट के बावजूद स्मृति से बाहर चला जाता है। –

+1

क्या आपने 'forEachOrdered() 'के साथ प्रयास किया है? – fge

उत्तर

4

आप एक समाधान है कि एक समारोह है कि यह राशि माना जाता नहीं है करने के लिए परिवर्तनशील राज्य नहीं जोड़ता है चाहते हैं, आप collect का सहारा हो सकता है:

static void distinctForSorted(IntStream s, IntConsumer action) { 
    s.collect(()->new long[]{Long.MIN_VALUE}, 
       (a, i)->{ if(a[0]!=i) { action.accept(i); assert i>a[0]; a[0]=i; }}, 
       (a, b)->{ throw new UnsupportedOperationException(); }); 
} 

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

यदि आप forEach कार्रवाई के बजाय IntStream का सामान्य उद्देश्य चाहते हैं, तो अतिरिक्त जटिलता के बावजूद Spliterator निम्न स्तर समाधान को प्राथमिकता दी जाती है।

static IntStream distinctForSorted(IntStream s) { 
    Spliterator.OfInt sp=s.spliterator(); 
    return StreamSupport.intStream(
     new Spliterators.AbstractIntSpliterator(sp.estimateSize(), 
     Spliterator.DISTINCT|Spliterator.SORTED|Spliterator.NONNULL|Spliterator.ORDERED) { 
     long last=Long.MIN_VALUE; 
     @Override 
     public boolean tryAdvance(IntConsumer action) { 
      long prev=last; 
      do if(!sp.tryAdvance(distinct(action))) return false; while(prev==last); 
      return true; 
     } 
     @Override 
     public void forEachRemaining(IntConsumer action) { 
      sp.forEachRemaining(distinct(action)); 
     } 
     @Override 
     public Comparator<? super Integer> getComparator() { 
      return null; 
     } 
     private IntConsumer distinct(IntConsumer c) { 
      return i-> { 
       if(i==last) return; 
       assert i>last; 
       last=i; 
       c.accept(i); 
      }; 
     } 
    }, false); 
} 

यह भी एक समानांतर समर्थन विरासत में हालांकि यह उन्हें एक सूत्र में प्रसंस्करण तो यह अलग आपरेशन में तेज़ी नहीं आएगी से पहले कुछ मान प्रीफ़ेचिंग से काम करता है, लेकिन शायद अनुवर्ती आपरेशन, अगर वहाँ गणना तीव्र होते हैं लोगों को।

static IntStream distinct(IntStream s) { 
    boolean parallel=s.isParallel(); 
    s=s.collect(BitSet::new, BitSet::set, BitSet::or).stream(); 
    if(parallel) s=s.parallel(); 
    return s; 
} 

:


पूरा होने के लिए, यहाँ मनमाने ढंग से, यानी अवर्गीकृत, IntStream रों जो "मुक्केबाजी प्लस HashMap" पर निर्भर नहीं करता के लिए एक अलग ऑपरेशन है इस प्रकार एक बहुत अच्छा स्मृति पदचिह्न हो सकता है यह केवल सकारात्मक int मानों के लिए काम करता है; इसे पूर्ण 32 बिट रेंज तक विस्तारित करने के लिए दो BitSet एस की आवश्यकता नहीं होगी, इस प्रकार संक्षेप में नहीं दिखता है, लेकिन अक्सर उपयोग केस 31 बिट रेंज तक या उससे भी कम तक सीमित करने की अनुमति देता है ...

+0

धन्यवाद। अब मैं देखता हूं कि एक कस्टम स्प्लिटरेटर ऐसा करने का तरीका है (जैसे स्टैकओवरफ्लो /q/28363323/1441122, ** स्टुअर्ट मार्क्स ** द्वारा सुझाए गए)। अंत में बिटसेट समाधान सुरुचिपूर्ण है, वैसे भी (हालांकि अभी भी स्मृति उपयोग में ओ (एन))। –

1

तरीका यह ठीक से करने के लिए एक spliterator में धारा चालू करने के लिए, तो वापस आ spliterator

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

एक बार जब आप कि spliterator है आप एक ही गुणों के साथ इसे वापस चालू कर सकते हैं एक धारा में है और उस पर

धारा संचालन करना जारी

चूंकि हम मौजूदा जेडीके-इंटरफेस को संशोधित नहीं कर सकते हैं क्योंकि सहायक एपीआई को इस तरह दिखना होगा: dedup(IntStream.of(...).map(...)).collect(...)


संदर्भ आधारित धाराओं के लिए आप java.util.stream.DistinctOps.makeRef(AbstractPipeline<?, T, ?>) के स्रोत का निरीक्षण किया, तो आप JDK कम या ज्यादा करता है कि देखेंगे।

यह केवल इंटस्ट्रीम कार्यान्वयन (java.util.stream.IntPipeline.distinct()) है जो एक अक्षम दृष्टिकोण लेता है जो DISTINCT या SORTED का लाभ नहीं लेता है।

यह सिर्फ एक इंटस्ट्रीम को Integer स्ट्रीम में अंधाधुंध रूपांतरित करता है और संदर्भ-आधारित deduplication का उपयोग उचित झंडे के साथ गुजरने के बिना करता है जो इसे स्मृति-कुशल बना देगा।

यह पहले से ही jdk9 में तय नहीं है, तो यह अगर वे बेकार में धारा-झंडे त्यागने यह अनिवार्य रूप से अनावश्यक स्मृति की खपत और धारा ऑप्स के लिए बर्बाद अनुकूलन क्षमता है, क्योंकि एक बग के लायक हो सकता है।

संबंधित मुद्दे