स्कैनर बनाम स्ट्रिंगटॉकेनाइज़र बनाम स्ट्रिंग


155

मैंने अभी जावा के स्कैनर वर्ग के बारे में सीखा है और अब मैं सोच रहा हूं कि यह स्ट्रिंगरोकेंनाइज़र और स्ट्रिंगसिंगिट के साथ तुलना / प्रतिस्पर्धा कैसे करता है। मुझे पता है कि StringTokenizer और String.Split केवल स्ट्रिंग्स पर काम करते हैं, इसलिए मैं एक स्ट्रिंग के लिए स्कैनर का उपयोग क्यों करना चाहूंगा? क्या स्कैनर सिर्फ विभाजन के लिए वन-स्टॉप-शॉपिंग होना है?

जवाबों:


240

वे पाठ्यक्रम के लिए अनिवार्य रूप से घोड़े हैं।

  • Scannerउन मामलों के लिए डिज़ाइन किया गया है जहाँ आपको एक स्ट्रिंग को पार्स करने की आवश्यकता होती है, विभिन्न प्रकारों के डेटा को खींचकर। यह बहुत लचीला है, लेकिन यकीनन आपको किसी विशेष अभिव्यक्ति द्वारा सीमांकित स्ट्रिंग्स की एक सरणी प्राप्त करने के लिए सरलतम एपीआई नहीं देता है।
  • String.split()और Pattern.split()आपको बाद में करने के लिए एक आसान वाक्यविन्यास देता है, लेकिन यह अनिवार्य रूप से वे सब करते हैं। यदि आप परिणामी तारों को पार्स करना चाहते हैं, या किसी विशेष टोकन के आधार पर सीमांकक को आधे रास्ते में बदलना चाहते हैं, तो वे आपकी मदद नहीं करेंगे।
  • StringTokenizerकी तुलना String.split()में और भी अधिक प्रतिबंधात्मक है , और उपयोग करने के लिए थोड़ा सा फिजूलखर्ची भी है। यह अनिवार्य रूप से तय सब्सट्रिंग्स द्वारा सीमांकित टोकन बाहर खींचने के लिए डिज़ाइन किया गया है। इस प्रतिबंध के कारण, यह लगभग दोगुना है String.split()। (मेरी तुलनाString.split()StringTokenizer देखें और ।) यह नियमित अभिव्यक्ति एपीआई से भी संबंधित है, जिसमें String.split()से एक हिस्सा है।

आप मेरे समय से ध्यान देंगे जो String.split()अभी भी एक विशिष्ट मशीन पर कुछ मिलीसेकंड में हजारों तारों का टोकन कर सकते हैं। इसके अलावा, इसका यह फायदा है StringTokenizerकि यह आपको एक स्ट्रिंग ऐरे के रूप में आउटपुट देता है, जो आमतौर पर आप चाहते हैं। एक का उपयोग करके Enumeration, जैसा कि प्रदान किया गया है StringTokenizer, अधिकांश समय "वाक्यबद्ध रूप से उधम मचाते" है। इस दृष्टिकोण से, StringTokenizerआजकल जगह की थोड़ी बर्बादी है, और आप बस उपयोग कर सकते हैं String.split()


8
String.Split और StringTokenizer पर आपके द्वारा चलाए गए समान परीक्षणों पर स्कैनर के परिणामों को देखना भी दिलचस्प होगा।
डेव

2
मुझे एक और सवाल का जवाब दिया: "जावा एपीआई नोटों में कहा गया है कि स्ट्रिंगट्रॉकेनाइजर का उपयोग क्यों हतोत्साहित किया जाता है?"। इस पाठ से ऐसा लगता है कि उत्तर "क्योंकि String.split () तेजी से पर्याप्त है" होगा।
पैर

1
तो क्या StringTokenizer अब बहुत अधिक हो गया है?
स्टीव मेकर

इसके बजाय क्या उपयोग करना है? चित्रान्वीक्षक?
एड्रियन

4
मुझे लगता है कि यह एक पुराने सवाल का जवाब है, लेकिन अगर मुझे मक्खी पर टोकन में एक विशाल पाठ स्ट्रीम को विभाजित करने की आवश्यकता है, तो क्या StringTokenizerअभी भी मेरी सबसे अच्छी शर्त नहीं है क्योंकि String.split()बस स्मृति से बाहर चलेगा?
सर्गेई टैचेनोव

57

चलो शुरू करते हैं StringTokenizer। यह पुराना हो रहा है और नियमित अभिव्यक्तियों का समर्थन भी नहीं करता है। इसके प्रलेखन में कहा गया है:

StringTokenizerएक विरासत वर्ग है जिसे संगतता कारणों से रखा जाता है, हालांकि इसका उपयोग नए कोड में हतोत्साहित किया जाता है। यह अनुशंसा की जाती है कि इस कार्यक्षमता को प्राप्त करने वाला कोई भी या इसके बजाय पैकेज की splitविधि का उपयोग करे ।Stringjava.util.regex

तो चलो इसे अभी बाहर फेंक दो। यही कारण है कि पत्तियों split()और Scanner। उनमें क्या अंतर है?

एक बात के लिए, split()बस एक सरणी देता है, जो कि फॉर्च लूप का उपयोग करना आसान बनाता है:

for (String token : input.split("\\s+") { ... }

Scanner एक धारा की तरह बनाया गया है:

while (myScanner.hasNext()) {
    String token = myScanner.next();
    ...
}

या

while (myScanner.hasNextDouble()) {
    double token = myScanner.nextDouble();
    ...
}

(इसकी एक बड़ी API है , इसलिए ऐसा मत सोचो कि यह हमेशा ऐसी सरल चीजों तक ही सीमित है।)

यह स्ट्रीम-स्टाइल इंटरफ़ेस सरल पाठ फ़ाइलों या कंसोल इनपुट को पार्स करने के लिए उपयोगी हो सकता है, जब आपके पास पार्स शुरू करने से पहले सभी इनपुट नहीं होते (या नहीं मिल सकते)।

व्यक्तिगत रूप से, केवल उसी समय का उपयोग Scannerकरना याद कर सकते हैं जब स्कूल प्रोजेक्ट्स के लिए है, जब मुझे कमांड लाइन से उपयोगकर्ता इनपुट प्राप्त करना था। यह उस तरह के ऑपरेशन को आसान बनाता है। लेकिन अगर मेरे पास ऐसा है Stringजिसे मैं विभाजित करना चाहता हूं, तो उसके साथ जाने के लिए लगभग कोई दिमाग नहीं है split()


20
StringTokenizer String.split () के रूप में तेजी से 2x है। यदि आप नियमित अभिव्यक्ति का उपयोग करने की जरूरत नहीं है, नहीं है!
एलेक्स वर्डेन

मैं बस Scannerएक पंक्ति में नई पंक्ति वर्णों का पता लगाता था String। नए लाइन वर्ण (कम से नज़र मंच के लिए मंच से भिन्न हो सकते हैं के बाद से Patternकी जावाडोक!) और इनपुट स्ट्रिंग के अनुरूप इसकी गारंटी नहीं है System.lineSeparator(), मुझे लगता है Scannerअधिक उपयुक्त के रूप में यह पहले से ही नए लाइन वर्ण जब फोन करने के लिए क्या देखने के लिए जानता है nextLine()। के लिए String.splitमैं सही regex पैटर्न में खिलाने के लिए लाइन विभाजक, जो मैं किसी भी मानक स्थान (सबसे अच्छा मैं है से इसे कॉपी कर सकते हैं में संग्रहीत नहीं पाते हैं पता लगाने के लिए होगा Scannerक्लास 'स्रोत)।
ADTC

9

StringTokenizer हमेशा से था। यह सभी का सबसे तेज़ है, लेकिन एन्यूमरेशन जैसा मुहावरा दूसरों की तरह सुरुचिपूर्ण नहीं हो सकता है।

विभाजन JDK 1.4 पर अस्तित्व में आया। टोकन की तुलना में धीमी लेकिन उपयोग करने में आसान है, क्योंकि यह स्ट्रिंग क्लास से कॉल करने योग्य है।

जेडीके 1.5 पर स्कैनर आया। यह सबसे लचीला है और प्रसिद्ध जावा स्कैन स्कैन फंक्शन परिवार के बराबर का समर्थन करने के लिए जावा एपीआई पर एक लंबे समय से स्थायी अंतराल को भरता है।


6

यदि आपके पास एक स्ट्रिंग ऑब्जेक्ट है जिसे आप टोकन करना चाहते हैं, तो स्ट्रिंग के विभाजन विधि का उपयोग करके स्ट्रिंग स्ट्रिंगराइज़र पर। यदि आप अपने प्रोग्राम के बाहर किसी स्रोत से, जैसे किसी फ़ाइल से, या उपयोगकर्ता से पाठ डेटा पार्स कर रहे हैं, तो यह एक स्कैनर काम आता है।


5
बस ऐसे ही, कोई औचित्य नहीं, कोई कारण नहीं?
जन.सुपोल

6

स्प्लिट धीमा है, लेकिन स्कैनर जितना धीमा नहीं है। StringTokenizer विभाजन की तुलना में तेज़ है। हालाँकि, मैंने पाया कि मैं कुछ लचीलेपन का व्यापार करके, गति बढ़ाने के लिए दोगुनी गति प्राप्त कर सकता था, जो मैंने JFastParser https://github.com/hughperkins/jfastparser पर किया था

एक स्ट्रिंग पर परीक्षण जिसमें एक मिलियन डबल्स हैं:

Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms

कुछ Javadoc अच्छा होता, और यदि आप संख्यात्मक डेटा के अलावा कुछ और पार्स करना चाहते हैं, तो क्या होगा?
NickJ

खैर, यह गति के लिए डिज़ाइन किया गया है, न कि सुंदरता के लिए। यह काफी सरल है, बस कुछ पंक्तियाँ, इसलिए यदि आप चाहें तो पाठ पार्सिंग के लिए कुछ और विकल्प जोड़ सकते हैं।
ह्यूग पर्किन्स

4

StringTokenizer की तुलना में String.split बहुत धीमी लगती है। विभाजन के साथ एकमात्र लाभ यह है कि आपको टोकन की एक सरणी मिलती है। इसके अलावा आप विभाजन में किसी भी नियमित अभिव्यक्ति का उपयोग कर सकते हैं। org.apache.commons.lang.StringUtils में एक स्प्लिट मेथड होता है जो कि दो में से किसी की तुलना में ज्यादा तेज काम करता है। StringTokenizer या String.split। लेकिन तीनों के लिए सीपीयू का उपयोग लगभग समान है। इसलिए हमें एक ऐसी विधि की भी आवश्यकता है जो सीपीयू की गहनता से कम हो, जिसे मैं अभी भी नहीं खोज पा रहा हूं।


3
यह उत्तर थोड़ा निरर्थक है। आप कहते हैं कि आप किसी ऐसी चीज़ की तलाश कर रहे हैं जो तेज़ हो लेकिन "कम सीपीयू इंटेंसिव" हो। किसी भी प्रोग्राम को CPU द्वारा निष्पादित किया जाता है। यदि कोई प्रोग्राम आपके CPU का 100% उपयोग नहीं करता है, तो उसे I / O जैसी किसी और चीज़ का इंतज़ार करना होगा। स्ट्रिंग टोकन के बारे में चर्चा करते समय यह कभी भी एक मुद्दा नहीं होना चाहिए, जब तक कि आप प्रत्यक्ष डिस्क एक्सेस नहीं कर रहे हैं (जो हम विशेष रूप से यहां नहीं कर रहे हैं)।
जोल्टा

4

मैंने हाल ही में अत्यधिक प्रदर्शन संवेदनशील स्थितियों में String.split () के खराब प्रदर्शन के बारे में कुछ प्रयोग किए। आपको यह उपयोगी लग सकता है।

http://eblog.chrononsystems.com/hidden-evils-of-javas-stringsplit-and-stringr

सार यह है कि String.split () हर बार एक रेगुलर एक्सप्रेशन पैटर्न संकलित करता है और इस प्रकार आपके प्रोग्राम को धीमा कर सकता है, इसकी तुलना में यदि आप एक पूर्व-निर्धारित पैटर्न ऑब्जेक्ट का उपयोग करते हैं और इसे सीधे स्ट्रिंग पर संचालित करने के लिए उपयोग करते हैं।


4
वास्तव में String.split () हमेशा पैटर्न संकलित नहीं करता है। स्रोत को देखें यदि 1.7 जावा, तो आप देखेंगे कि एक चेक है यदि पैटर्न एक एकल वर्ण है और एक बच नहीं है, तो यह रिगेक्स के बिना स्ट्रिंग को विभाजित करेगा, इसलिए यह काफी तेज होना चाहिए।
Krzysztof Krasoń

1

डिफ़ॉल्ट परिदृश्यों के लिए मैं पैटर्न (एसपीएलआईटी) का सुझाव दूंगा, लेकिन अगर आपको अधिकतम प्रदर्शन की आवश्यकता है (विशेष रूप से एंड्रॉइड के सभी समाधान जो मैंने परीक्षण किए हैं, वे काफी धीमी हैं) और आपको केवल एक चार्ट द्वारा विभाजित करने की आवश्यकता है, मैं अब अपनी खुद की विधि का उपयोग करता हूं:

public static ArrayList<String> splitBySingleChar(final char[] s,
        final char splitChar) {
    final ArrayList<String> result = new ArrayList<String>();
    final int length = s.length;
    int offset = 0;
    int count = 0;
    for (int i = 0; i < length; i++) {
        if (s[i] == splitChar) {
            if (count > 0) {
                result.add(new String(s, offset, count));
            }
            offset = i + 1;
            count = 0;
        } else {
            count++;
        }
    }
    if (count > 0) {
        result.add(new String(s, offset, count));
    }
    return result;
}

स्ट्रिंग के लिए चार्ट प्राप्त करने के लिए "abc" .toCharArray () का उपयोग करें। उदाहरण के लिए:

String s = "     a bb   ccc  dddd eeeee  ffffff    ggggggg ";
ArrayList<String> result = splitBySingleChar(s.toCharArray(), ' ');

1

एक महत्वपूर्ण अंतर यह है कि String.split () और Scanner दोनों खाली तारों का उत्पादन कर सकते हैं लेकिन StringTokenizer कभी भी ऐसा नहीं करता है।

उदाहरण के लिए:

String str = "ab cd  ef";

StringTokenizer st = new StringTokenizer(str, " ");
for (int i = 0; st.hasMoreTokens(); i++) System.out.println("#" + i + ": " + st.nextToken());

String[] split = str.split(" ");
for (int i = 0; i < split.length; i++) System.out.println("#" + i + ": " + split[i]);

Scanner sc = new Scanner(str).useDelimiter(" ");
for (int i = 0; sc.hasNext(); i++) System.out.println("#" + i + ": " + sc.next());

आउटपुट:

//StringTokenizer
#0: ab
#1: cd
#2: ef
//String.split()
#0: ab
#1: cd
#2: 
#3: ef
//Scanner
#0: ab
#1: cd
#2: 
#3: ef

ऐसा इसलिए है क्योंकि String.split () और Scanner.useDelimiter () के लिए सीमांकक केवल एक स्ट्रिंग नहीं है, बल्कि एक नियमित अभिव्यक्ति है। हम ऊपर दिए गए उदाहरण में सीमांकक "" को "+" से बदल सकते हैं ताकि उन्हें स्ट्रिंगरोकाइन्ज़र की तरह व्यवहार किया जा सके।


-5

String.split () बहुत अच्छा काम करता है, लेकिन इसकी अपनी सीमाएँ हैं, जैसे कि यदि आप एक स्ट्रिंग को विभाजित करना चाहते हैं जैसा कि सिंगल या डबल पाइप (!) प्रतीक के आधार पर नीचे दिखाया गया है, यह काम नहीं करता है। इस स्थिति में आप StringTokenizer का उपयोग कर सकते हैं।

एबीसी | IJK


12
वास्तव में, आप अपने उदाहरण को "ABC | IJK" के साथ विभाजित कर सकते हैं। split ("\\ |");
टॉमो

"ABC || DEF ||" .split ("\\") वास्तव में काम नहीं करता है, क्योंकि यह दो खाली मानों को अनदेखा कर देगा, जिससे पार्सिंग की तुलना में अधिक हास्य होना चाहिए।
आर्मंड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.