2014-05-23 25 views
16

मैं एक बार में एक बाइट या एक कैरेक्टर को संसाधित करने के लिए InputStream के आसपास java.util.streams.Stream लपेटना चाहता हूं। मुझे ऐसा करने का कोई आसान तरीका नहीं मिला।मैं इनपुट 8 स्ट्रीम के साथ जावा 8 स्ट्रीम का उपयोग कैसे कर सकता हूं?

निम्नलिखित अभ्यास पर विचार करें: हम प्रत्येक फ़ाइल को टेक्स्ट फ़ाइल में कितनी बार प्रकट करते हैं, इसकी गणना करना चाहते हैं। हम इसे एक सरणी में संग्रहीत कर सकते हैं ताकि tally[0] फ़ाइल में दिखाई देने वाली संख्या को संग्रहीत करेगा, tally[1] समय बी की संख्या प्रदर्शित करता है और इसी तरह।

int[] tally = new int[26]; 
Stream<String> lines = Files.lines(Path.get(aFile)).map(s -> s.toLowerCase()); 
Consumer<String> charCount = new Consumer<String>() { 
    public void accept(String t) { 
     for(int i=0; i<t.length(); i++) 
     if(Character.isLetter(t.charAt(i)) 
      tall[t.charAt(i) - 'a' ]++; 
    } 
}; 
lines.forEach(charCount); 

वहाँ lines विधि का उपयोग कर के बिना इस को पूरा करने का एक तरीका है: जब से मैं सीधे फ़ाइल स्ट्रीमिंग का एक तरीका नहीं मिल सकता है, मैं इस किया था? क्या मैं पाठ फ़ाइल में प्रत्येक पंक्ति के लिए स्ट्रिंग बनाने के बजाय सीधे प्रत्येक चरित्र को स्ट्रीम या स्ट्रीम के रूप में संसाधित कर सकता हूं।

क्या मैं java.io.InputStream को java.util.Stream.stream में और अधिक सख्ती से परिवर्तित कर सकता हूं?

+1

सावधान रहें! 'Character.isLetter' केवल' a-z' से अधिक के लिए 'सत्य' देता है, उदा। 'ä' या' π'। – Holger

+0

ठीक है, मैंने सोचा था कि पहले कम मामले में कनवर्ट करना उस पर ध्यान रखेगा। शायद मैं चाहता हूँ .isLowerCase? – Thorn

+2

नहीं, बिंदु यह है कि जावा यूनिकोड का उपयोग करता है और 26 से अधिक अक्षर हैं। लोअरकेस में रूपांतरण उनके लिए सही काम करेगा, उदा। ''Ä'' से' 'ä'' और' 'Π'' से' π'' में कनवर्ट करें। लेकिन यदि आप ''a'' और' z 'के बीच 26 मानों को गिनना चाहते हैं तो आपको केवल' isLetter' का उपयोग करने के बजाय उस सीमा के लिए जांच करना चाहिए (जैसे मैंने अपने उत्तर में किया था)। ''ä'' और' 'π'' * * लोअरकेस अक्षर हैं ... – Holger

उत्तर

17

सबसे पहले, आपको अपना कार्य फिर से परिभाषित करना होगा। आप वर्ण पढ़ रहे हैं, इसलिए आप InputStream को परिवर्तित नहीं करना चाहते हैं लेकिन Reader को Stream में परिवर्तित करना नहीं चाहते हैं।

आप होने वाले वर्णसेट रूपांतरण को फिर से कार्यान्वित नहीं कर सकते हैं, उदा। InputStreamReader में, Stream संचालन के साथ एस एस 10 और परिणामी char एस के बीच m: m मैपिंग हो सकता है।

Reader से बाहर स्ट्रीम बनाना थोड़ा मुश्किल है।

PrimitiveIterator.OfInt it=new PrimitiveIterator.OfInt() { 
    int last=-2; 
    public int nextInt() { 
     if(last==-2 && !hasNext()) 
      throw new NoSuchElementException(); 
     try { return last; } finally { last=-2; } 
    } 
    public boolean hasNext() { 
     if(last==-2) 
     try { last=reader.read(); } 
     catch(IOException ex) { throw new UncheckedIOException(ex); } 
     return last>=0; 
    } 
}; 

एक बार जब आप इटरेटर है आप एक spliterator के चक्कर का उपयोग कर एक धारा बना सकते हैं और अपने वांछित आपरेशन प्रदर्शन कर सकते हैं:

आप एक आइटम और एक अंत हालत हो रही है के लिए एक विधि निर्दिष्ट करने के लिए एक iterator की आवश्यकता होगी
int[] tally = new int[26]; 
StreamSupport.intStream(Spliterators.spliteratorUnknownSize(
    it, Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL), false) 
// now you have your stream and you can operate on it: 
    .map(Character::toLowerCase) 
    .filter(c -> c>='a'&&c<='z') 
    .map(c -> c-'a') 
    .forEach(i -> tally[i]++); 

ध्यान दें कि जब iterators, अधिक परिचित नई Spliterator इंटरफेस को लागू कर रहे हैं सीधे आपरेशन को सरल रूप में यह दो तरीकों कि मनमाने ढंग से आदेश में कहा जा सकता है के बीच राज्य बनाए रखने के लिए आवश्यकता नहीं है। इसके बजाय, हम सिर्फ एक tryAdvance विधि है जो सीधे एक read() कॉल करने के लिए मैप किया जा सकता है:

Spliterator.OfInt sp = new Spliterators.AbstractIntSpliterator(1000L, 
    Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL) { 
     public boolean tryAdvance(IntConsumer action) { 
      int ch; 
      try { ch=reader.read(); } 
      catch(IOException ex) { throw new UncheckedIOException(ex); } 
      if(ch<0) return false; 
      action.accept(ch); 
      return true; 
     } 
    }; 
StreamSupport.intStream(sp, false) 
// now you have your stream and you can operate on it: 
… 

हालांकि, ध्यान दें कि आप एक बहुत आसान जीवन हो सकता है यदि आप अपना विचार बदल सकते हैं और Files.lines उपयोग करने के लिए तैयार हैं :

int[] tally = new int[26]; 
Files.lines(Paths.get(file)) 
    .flatMapToInt(CharSequence::chars) 
    .map(Character::toLowerCase) 
    .filter(c -> c>='a'&&c<='z') 
    .map(c -> c-'a') 
    .forEach(i -> tally[i]++); 
+1

आपके उत्तर का अंतिम भाग वही है जो मैं ढूंढ रहा था।मैंने नहीं देखा कि धाराओं का उपयोग करके एक पंक्ति में प्रत्येक स्ट्रिंग के माध्यम से कैसे पुनरावृत्ति करें। – Thorn

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