2016-02-05 15 views
9

मुझे आश्चर्य है कि क्या नए स्ट्रीम एपीआई का उपयोग "समूह" मानों के अनुक्रमों के लिए करने के लिए किसी भी निफ्टी तरीके से है।मूल्यों के समूह अनुक्रम

उदा। पूर्णांकों की एक श्रृंखला, पूर्णांक समूहों में विभाजित जहां प्रत्येक समूह के एक आरोही संख्या अनुक्रम है:

IntStream seq = IntStream.of(1, 2, 3, -1, -1, 1, 2, 1, 2); 
IntFunction next = i -> i + 1; 

// DESIRED OUTPUT: [[1,2,3], [-1], [-1], [1,2], [1,2]] 
+0

आउटपुट इस तरह दिखने चाहिए: '[[1,2,3], [-1], [-1,1,2], [1,2]]'? – Flown

+3

@ फ्लाउन नहीं क्योंकि '1! = Next.apply (-1)' – Tunaki

+0

आह ठीक है 'अगला' एक अनुमान है। – Flown

उत्तर

7

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

public static void main(String[] args) { 
    IntStream seq = IntStream.of(1, 2, 3, -1, -1, 1, 2, 1, 2); 
    IntUnaryOperator next = i -> i + 1; 

    List<List<Integer>> result = 
     IntStreamEx.of(seq).boxed().groupRuns((i1, i2) -> next.applyAsInt(i1) == i2).toList(); 

    System.out.println(result); // prints "[[1, 2, 3], [-1], [-1], [1, 2], [1, 2]]" 
} 

यह समूह:

हालांकि, आप इस के लिए StreamEx लाइब्रेरी का उपयोग कर सकते हैं। अंत में, यह स्ट्रीम List में एकत्र की जाती है।

+0

आपके विचार के रूप में इतना सुंदर नहीं है, लेकिन यह वास्तव में शुद्ध जावा -8 धाराओं के साथ किया जा सकता है। – Andremoniy

+0

धन्यवाद! ऐसा लगता है कि StreamEx मुझे बहुत सारे सिरदर्द बचाएगा! – rednoah

1

ऐसा नहीं है @Tunaki समाधान के रूप में सुंदर है, लेकिन का उपयोग कर "शुद्ध" जावा-8 धाराओं:

IntStream seq = IntStream.of(1, 2, 3, -1, -1, 1, 2, 1, 2); 

Deque<Deque<Integer>> r = new ArrayDeque<>(singleton(new ArrayDeque<>())); 

seq.filter(i -> !r.getLast().isEmpty() && r.getLast().getLast() + 1 != i || !r.getLast().add(i)) 
      .forEach(i -> r.add(new ArrayDeque<>(singleton(i)))); 

System.out.println(r); // prints: [[1, 2, 3], [-1], [-1], [1, 2], [1, 2]] 

यहाँ बस कोड की शिष्टता के लिए मैं क्रम में Deque वर्ग का उपयोग करें (List के लिए getLast() विधि का उपयोग करने के लिए इसे होगा इतना कॉम्पैक्ट नहीं हो)।

+2

ध्यान दिया जाना चाहिए कि इस तरह के समाधान एपीआई का दुरुपयोग करते हैं (विशेष रूप से, 'filicate' 'filicate' को पास किया गया है [spec] के अनुसार स्टेटलेस होना चाहिए (https://docs.oracle.com/javase/8/docs/api/ जावा/util/धारा/Stream.html # फिल्टर-java.util.function.Predicate-))। नतीजतन इस समाधान को समांतर नहीं किया जा सकता है। –

+0

@TagirValeev तनुकी के समानांतर हो सकता है? – Andremoniy

+1

हाँ, और आप वास्तव में बड़े इनपुट पर गतिशील होने की संभावना होगी। प्रत्येक StreamEx सुविधा समानांतरता को सही तरीके से संभालती है और उनमें से अधिकांश वास्तव में समांतरता से लाभान्वित होते हैं। –

6

यदि आप किसी मेमोरी डेटा संरचना, जैसे किसी सरणी या सूची पर काम करने के इच्छुक हैं, तो इसे केवल कुछ चरणों में मानक जावा 8 में करना संभव है। यह सरणी प्रोग्रामिंग तकनीकों का उपयोग करके किया जा सकता है जैसे कि मेरे answer to this question में सचित्र। Flown's answer to this question में उपयोग की जाने वाली कुछ चालाक सशर्तियों का उपयोग किनारे के मामलों को एक साफ तरीके से ख्याल रखता है।

मुख्य अंतर्दृष्टि यह जानना है कि एक नया सेगमेंट (या समूह) प्रत्येक बिंदु पर शुरू होता है जहां वांछित भविष्यवाणी नहीं मिली है। यही है, एक नया सेगमेंट शुरू होता है जहां seq[i-1] + 1 != seq[i] है। के इनपुट पर एक IntStream चलाते हैं और इस संपत्ति के लिए अनुक्रमणिका को फ़िल्टर और कुछ सरणी x में परिणाम की दुकान:

int[] seq = { 1, 2, 3, -1, -1, 1, 2, 1, 2 }; 
    int[] x = IntStream.range(1, seq.length) 
         .filter(i -> seq[i-1] + 1 != seq[i]) 
         .toArray(); 

में जिसके परिणामस्वरूप

[3, 4, 5, 7] 

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

int[] x = IntStream.rangeClosed(0, seq.length) 
         .filter(i -> i == 0 || i == seq.length || 
            seq[i-1] + 1 != seq[i]) 
         .toArray(); 

    [0, 3, 4, 5, 7, 9] 

अब अनुक्रमित के हर आसन्न जोड़ी मूल सरणी के एक subrange है। हम उन subranges को निकालने के लिए, वांछित परिणाम दे रही है एक और धारा का उपयोग कर सकते हैं:

int[][] result = 
     IntStream.range(0, x.length - 1) 
       .mapToObj(i -> Arrays.copyOfRange(seq, x[i], x[i+1])) 
       .toArray(int[][]::new); 

    [[1, 2, 3], [-1], [-1], [1, 2], [1, 2]] 

यह एक समारोह है कि अपने आप में एक "अगला" समारोह है कि क्षेत्र में अगले मूल्य की गणना करता है लेता में निकाला जा सकता है। यही है, किसी भी तत्व के लिए, यदि उसके दाहिने तत्व का तत्व अगले-फ़ंक्शन के परिणाम से मेल खाता है, तो तत्व एक ही सेगमेंट में हैं; अन्यथा यह एक सेगमेंट सीमा है।कोड यह रहा:

int[][] segments(int[] seq, IntUnaryOperator next) { 
    int[] x = IntStream.rangeClosed(0, seq.length) 
         .filter(i -> i == 0 || i == seq.length || 
           next.applyAsInt(seq[i-1]) != seq[i]) 
         .toArray(); 

    return IntStream.range(0, x.length - 1) 
        .mapToObj(i -> Arrays.copyOfRange(seq, x[i], x[i+1])) 
        .toArray(int[][]::new); 
} 

आप इसे इस तरह फोन चाहते हैं:

int[] seq = { 1, 2, 3, -1, -1, 1, 2, 1, 2 }; 
    System.out.println(Arrays.deepToString(segments(seq, i -> i + 1))); 

    [[1, 2, 3], [-1], [-1], [1, 2], [1, 2]] 

अगली समारोह बदलने की अनुमति देता है एक अलग तरह से खंडों बंटवारे। उदाहरण के लिए, बराबर मूल्यों के खंडों में एक सरणी विभाजित करने के लिए, यदि आप ऐसा करते हैं:

int[] seq = { 2, 2, 1, 3, 3, 1, 1, 1, 4, 4, 4 }; 
    System.out.println(Arrays.deepToString(segments(seq, i -> i))); 

    [[2, 2], [1], [3, 3], [1, 1, 1], [4, 4, 4]] 

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

int[][] segments(int[] input, BiPredicate<Integer, Integer> pred) { 
    int[] x = IntStream.rangeClosed(0, input.length) 
         .filter(i -> i == 0 || i == input.length || 
           !pred.test(input[i-1], input[i])) 
         .toArray(); 

    return IntStream.range(0, x.length - 1) 
        .mapToObj(i -> Arrays.copyOfRange(input, x[i], x[i+1])) 
        .toArray(int[][]::new); 
} 

यह एक अलग कसौटी का उपयोग कर, उदाहरण के लिए, होगा- क्षेत्रों में वृद्धि सभा क्षेत्रों की अनुमति देता है:

int[] seq = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3 }; 
    System.out.println(Arrays.deepToString(segments(seq, (a, b) -> b > a))); 

    [[3], [1, 4], [1, 5, 9], [2, 6], [5], [3]] 

यह दो int मानों पर एक आदिम द्वि-भविष्यवाणी का उपयोग करने के लिए विशेषीकृत किया जा सकता है, या इसे किसी भी प्रकार के इनपुट के किसी भी प्रकार के BiPredicate का उपयोग करने की अनुमति देने के लिए सामान्यीकृत किया जा सकता है।

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