2015-08-19 9 views
7

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

IntStream myStream = IntStream.range(0,3); 
myStream.filter(s -> { 
    System.out.print("[filtering "+s+"] "); 
    myStream.forEach(q -> System.out.print(q+", ")); 
    System.out.println(); 
    return true; //eventually respond to values observed on the line above 
}); 

:

मैं कुछ इस तरह पूरा करने के लिए इच्छुक हूँ

[filtering 0] 
[filtering 1] 0, 
[filtering 2] 0, 1, 
[filtering 3] 0, 1, 2, 

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

local variables referenced from a lambda expression must be final or effectively final 

इसका कारण यह है कि मैं एक फिल्टर है कि पहले से ही myStream पर अभिनय के भीतर myStream संदर्भित कर रहा हूँ हो गया लगता है:

हालांकि, ऊपर के उदाहरण मुझे NetBeans में एक त्रुटि देता है। क्या इस त्रुटि के आसपास काम करने का कोई अच्छा तरीका है (यानी स्ट्रीम की अंतिम प्रतिलिपि बनाना जिसमें केवल अब तक फ़िल्टर किए गए मान हैं), या स्टोर के लिए एक अलग संग्रह का उपयोग किए बिना इस तरह की समस्या का बेहतर दृष्टिकोण है मान?

+3

नहीं, एक अलग संग्रह का उपयोग करने से बेहतर दृष्टिकोण नहीं है। स्ट्रीम एपीआई उस तरह के उपयोग के लिए डिज़ाइन नहीं किया गया है। –

+2

आपके पास कोड के साथ, आप 'अंतिम 'कीवर्ड को' इंटस्ट्रीम myStream' से पहले रख सकते हैं। लेकिन कोड वैसे भी सही नहीं होगा, क्योंकि आपको दूसरी पंक्ति को 'myStream = myStream.filter (...)' में बदलने की आवश्यकता है, अन्यथा आप फ़िल्टरिंग के बिना स्ट्रीम का उपयोग करेंगे। –

+0

http: // stackoverflow।कॉम/ए/20007272/2711488 दूसरा आधा ... – Holger

उत्तर

2

आप स्ट्रीम को एक से अधिक बार संसाधित नहीं कर सकते हैं, इसलिए फ़िल्टर विधि के अंदर myStream.forEach को कॉल करना संभव नहीं है।

आप फ़िल्टर के अंदर एक नया IntStream बना सकते हैं।

ध्यान दें कि आप क्रम में बाहरी स्ट्रीम पाइप लाइन के लिए कुछ टर्मिनल आपरेशन जोड़ने के लिए के लिए यह कार्रवाई की जानी चाहिए:

IntStream myStream = IntStream.range(0,4); 
myStream.filter(s -> { 
    System.out.print("[filtering "+s+"] "); 
    IntStream.range(0,s).forEach(q -> System.out.print(q+", ")); 
    System.out.println(); 
    return true; //eventually respond to values observed on the line above 
}).forEach(i->{}); 

यह पैदा करता है:

[filtering 0] 
[filtering 1] 0, 
[filtering 2] 0, 1, 
[filtering 3] 0, 1, 2, 
+3

यह ओपी द्वारा अनुरोध किए गए "पहले फ़िल्टर किए गए मानों के माध्यम से लूप के लिए एक अच्छा तरीका खोजने में" मदद नहीं करता है। –

0

अन्य उत्तर सुझाव दिया है कि जिस दृष्टिकोण से मैं कोशिश कर रहा था वह संभव नहीं है, और एक अलग संग्रह का उपयोग किया जाना चाहिए।

अधिक संपूर्ण उत्तर प्रदान करने के लिए, मैं धाराओं का उपयोग करके इस समस्या का एक वैध दृष्टिकोण प्रदान करना चाहता था और इसे एक और पारंपरिक दृष्टिकोण के साथ तुलना करना चाहता था।

लिस्टिंग धाराओं का इस्तेमाल करके (एरेटोस्थेनेज की चलनी का उपयोग) अभाज्य संख्या:

List<Integer> primes = new ArrayList<Integer>(); 

IntStream.iterate(2, i -> i + 1) 
    .limit(UPPER_BOUND) 
    .filter(i -> { 
     for(int j=0; j<primes.size(); j++) { 
      int prime = primes.get(j); 

      if(prime > Math.sqrt(i)) { 
       break; 
      } 

      if(i % prime == 0) { 
       return false; 
      } 
     } 
     return true; 
    }) 
    .forEach(primes::add); 

पारंपरिक, बराबर, दृष्टिकोण धाराओं का उपयोग किए बिना:

List<Integer> primes = new ArrayList<Integer>(); 

for(int i=2; i < UPPER_BOUND; i++) { 
    boolean isPrime = true; 

    for(int j=0; j<primes.size(); j++) { 
     int prime = primes.get(j); 

     if(prime > Math.sqrt(i)) { 
      break; 
     } 

     if(i % prime == 0) { 
      isPrime = false; 
      break; 
     } 
    } 

    if(isPrime) { 
     primes.add(i); 
    } 
} 

प्रदर्शन तुलना:

तो मुझे प्रत्येक समारोह के साथ प्रयोग ने लगातार प्रदर्शन किया कि परंपरागत दृष्टिकोण इस मामले में धाराओं का उपयोग करने से वास्तव में तेज़ है। धाराओं के दृष्टिकोण लगातार 1 ले लिया।पारंपरिक दृष्टिकोण की तुलना में सभी प्राइम संख्याओं को दस लाख से अधिक समय तक ढूंढने के लिए 5x लंबा (मेरी मशीन पर क्रमश: 106ms और 70ms का औसत)।

स्ट्रीम में यह अंतर आसानी से बनाया जा सकता है यदि स्ट्रीम का .parallel() फ़ंक्शन समस्या के आसान समानांतरता की अनुमति दे सकता है। हालांकि, इस मामले में समांतरता आसान नहीं है क्योंकि ArrayList थ्रेड-सुरक्षित नहीं है, और जल्दी ही त्रुटियों और/या गलत परिणामों में परिणाम देगा।

निष्कर्ष:

मान लिया जाये कि अन्य उत्तर सही हैं, कि एक ही धारा पर एक फिल्टर के भीतर छानने पहले से फ़िल्टर किए गए डेटा जावा में संभव नहीं है।

लिस्टिंग प्राइम स्ट्रीम का उपयोग करके निपटाया जा सकता है। हालांकि, अपने आप से बेहतर समाधान लंबित है, वर्तमान में पारंपरिक धारा-कम दृष्टिकोण के साथ रहना बेहतर है।

3

मैं इरेटोस्टेनेस की चाकू का उपयोग करके प्राइम संख्याओं का एक अनंत Stream बनाने में कामयाब रहा, लेकिन यह वास्तव में पिछले मूल्यों का उपयोग नहीं करता है। इसके बजाए, यह पूंछ में एक प्राइम के गुणकों को हटा देता है (आलसी तरीके से, क्योंकि पूंछ अनंत है), जैसे एरेटोस्टेनेस एल्गोरिदम की मूल चलनी। इसके लिए, मैंने सहायक के रूप में Iterator का उपयोग किया (क्योंकि Stream केवल एक बार उपयोग किया जा सकता है) और धाराओं के लिए lazyConcat लागू किया गया।

class StreamUtils { 
    public static IntStream fromIterator(PrimitiveIterator.OfInt it) { 
     return StreamSupport.intStream(
       Spliterators.spliteratorUnknownSize(it, Spliterator.ORDERED), false); 
    } 

    public static IntStream lazyConcat(Supplier<IntStream> a, Supplier<IntStream> b) { 
     return StreamSupport.intStream(new Spliterator.OfInt() { 
      boolean beforeSplit = true; 
      Spliterator.OfInt spliterator; 

      @Override 
      public OfInt trySplit() { 
       return null; 
      } 

      @Override 
      public long estimateSize() { 
       return Long.MAX_VALUE; 
      } 

      @Override 
      public int characteristics() { 
       return Spliterator.ORDERED; 
      } 

      @Override 
      public boolean tryAdvance(IntConsumer action) { 
       boolean hasNext; 
       if (spliterator == null) { 
        spliterator = a.get().spliterator(); 
       } 
       hasNext = spliterator.tryAdvance(action); 
       if (!hasNext && beforeSplit) { 
        beforeSplit = false; 
        spliterator = b.get().spliterator(); 
        hasNext = spliterator.tryAdvance(action); 
       } 
       return hasNext; 
      } 
     }, false); 
    } 
} 

एरेटोस्थेनेज धारा के मेरे चलनी इस तरह दिखता है:

System.out.println(Primes.stream().limit(20).boxed().collect(Collectors.toList())); 

आउटपुट::

[2, 3

class Primes { 
    public static IntStream stream() { 
     return sieve(IntStream.iterate(2, n -> n + 1)); 
    } 

    private static IntStream sieve(IntStream s) { 
     PrimitiveIterator.OfInt it = s.iterator(); 
     int head = it.nextInt(); 
     IntStream tail = StreamUtils.fromIterator(it); 
     return StreamUtils.lazyConcat(
       () -> IntStream.of(head), 
       () -> sieve(tail.filter(n -> n % head != 0))); 
    } 
} 

तो हम इसे इस तरह उपयोग कर सकते हैं , 5, 7, 11, 13, 17, 1 9, 23, 2 9, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]

मुझे लगता है कि यह एक अच्छा अभ्यास था, लेकिन ऐसा लगता है कि यह काफी अक्षम है और बिल्कुल भी अनुकूल नहीं है।

+2

स्ट्रीम एपीआई पुरस्कार का सबसे खराब दुरुपयोग आपके पास जाता है! मेरे लिए यह प्राइम नंबर 5620 9 के बाद 'स्टैक ओवरफ्लो एरर' के साथ मर जाता है। फिर भी ऊपर उठाया गया। –

+2

@TagirValeev हाहा हां, यह निश्चित रूप से जावा में जाने का तरीका नहीं है। लेकिन जैसा कि मेरे पास कार्यात्मक प्रोग्रामिंग में पृष्ठभूमि है जहां अनंत सूचियां एक आम बात है, मैं इसे जावा स्ट्रीम के साथ पुन: पेश करने की कोशिश कर रहा था। –

1

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

List<Integer> primes = IntStream.range(2, UPPER_BOUND) 
    .collect(ArrayList::new, 
      (list, number) -> { 
       for(int j=0; j < list.size(); j++) { 
        int prime = list.get(j); 

        if(prime > Math.sqrt(number)) { 
         break; 
        } 

        if(number % prime == 0) { 
         return; 
        } 
       } 

       list.add(number); 
      }, 
      List::addAll); 

ArrayList::new एक नई सूची जो तब list के रूप में उपभोक्ता द्वारा संदर्भित है बनाता है। उपभोक्ता को number तत्व के साथ धारा में प्रत्येक तत्व के लिए बुलाया जाता है।

List::addAll केवल समांतर धाराओं के लिए प्रासंगिक होगा जिसका उपयोग इस एल्गोरिदम के लिए भी नहीं किया जा सकता है।

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