2014-06-09 4 views
7

स्ट्रिंग वर्ग कुछ तरीकों कि मैं नहीं समझ सकता है कि वे इस तरह से लागू किया गया है ... की जगह उनमें से एक है।JVM स्ट्रिंग तरीकों कार्यान्वयन

public String replace(CharSequence target, CharSequence replacement) { 
    return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
      this).replaceAll(Matcher.quoteReplacement(replacement.toString())); 
} 

क्या सरल और अधिक कुशल (तेज़!) विधि पर कुछ महत्वपूर्ण फायदे हैं?

public static String replace(String string, String searchFor, String replaceWith) { 

    StringBuilder result=new StringBuilder(); 

    int index=0; 
    int beginIndex=0; 
    while((index=string.indexOf(searchFor, index))!=-1){ 
     result.append(string.substring(beginIndex, index)+replaceWith); 
     index+=searchFor.length(); 
     beginIndex=index; 
    } 
    result.append(string.substring(beginIndex, string.length())); 

    return result.toString(); 

} 

आँकड़े जावा 7 के साथ: "AXC"

टाइम्स:
string.replace:
1000000 पुनरावृत्तियों
साथ "x" "abc" में
परिणाम "बी" की जगह 485ms
string.replaceAll: 490ms
वें तरह की जगह = 180ms

कोड अनुकूलित

public String replaceAll(String regex, String replacement) { 
    return Pattern.compile(regex).matcher(this).replaceAll(replacement); 
} 

विभाजन कार्यान्वयन होना चाहिए::

public String[] split(String regex, int limit) { 
    return Pattern.compile(regex).split(this, limit); 
} 

public String[] split(String regex, int limit) { 
    /* fastpath if the regex is a 
    (1)one-char String and this character is not one of the 
     RegEx's meta characters ".$|()[{^?*+\\", or 
    (2)two-char String and the first char is the backslash and 
     the second is not the ascii digit or ascii letter. 
    */ 
    char ch = 0; 
    if (((regex.value.length == 1 && 
     ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) || 
     (regex.length() == 2 && 
      regex.charAt(0) == '\\' && 
      (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 && 
      ((ch-'a')|('z'-ch)) < 0 && 
      ((ch-'A')|('Z'-ch)) < 0)) && 
     (ch < Character.MIN_HIGH_SURROGATE || 
     ch > Character.MAX_LOW_SURROGATE)) 
    { 
     int off = 0; 
     int next = 0; 
     boolean limited = limit > 0; 
     ArrayList<String> list = new ArrayList<>(); 
     while ((next = indexOf(ch, off)) != -1) { 
      if (!limited || list.size() < limit - 1) { 
       list.add(substring(off, next)); 
       off = next + 1; 
      } else { // last one 
       //assert (list.size() == limit - 1); 
       list.add(substring(off, value.length)); 
       off = value.length; 
       break; 
      } 
     } 
     // If no match was found, return this 
     if (off == 0) 
      return new String[]{this}; 

     // Add remaining segment 
     if (!limited || list.size() < limit) 
      list.add(substring(off, value.length)); 

     // Construct result 
     int resultSize = list.size(); 
     if (limit == 0) 
      while (resultSize > 0 && list.get(resultSize - 1).length() == 0) 
       resultSize--; 
     String[] result = new String[resultSize]; 
     return list.subList(0, resultSize).toArray(result); 
    } 
    return Pattern.compile(regex).split(this, limit); 
} 

की जगह विधि के तर्क के बाद: ई जावा 7 विभाजन विधि भारी पैटर्न संकलन/regex प्रसंस्करण से बचने के लिए जब संभव अनुकूलित है

प्रदर्शन हानि प्रतिस्थापन विधियों पर पाए गए लोगों से बहुत दूर नहीं हैं। किसी कारण से ओरेकल कुछ तरीकों पर नहीं, बल्कि कुछ अन्य तरीकों पर फास्टपाथ दृष्टिकोण देता है।

+3

"जावा मूल विधि के कार्यान्वयन के कारण क्या हैं?" <- जावा टीम से पूछें? –

+0

उनकी 'प्रतिस्थापन() 'उनके' replaceAll()' का उपयोग करता है। वहां क्या गलत है? प्रतिस्थापन के लिए कोड डुप्लिकेट क्यों करें? –

+0

विधि दक्षता? – marcolopes

उत्तर

7

क्या आप सुनिश्चित हैं कि आपकी प्रस्तावित विधि String कक्षा द्वारा उपयोग की जाने वाली रेगेक्स-आधारित एक की तुलना में वास्तव में तेज़ है - न केवल आपके स्वयं के परीक्षण इनपुट के लिए, बल्कि प्रत्येक संभावित इनपुट के लिए जो कोई प्रोग्राम उस पर फेंक सकता है? यह सबस्ट्रिंग मिलान करने के लिए String.indexOf पर निर्भर करता है, जो स्वयं एक निष्पक्ष कार्यान्वयन है जो खराब बुरी स्थिति के प्रदर्शन के अधीन है। यह पूरी तरह से संभव है कि Pattern अनावश्यक तुलना से बचने के लिए KMP जैसे अधिक परिष्कृत मिलान वाले एल्गोरिदम लागू करता है।

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

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

यह बहुत अच्छा हो सकता है कि replace के लिए एक ही विशेष मामला अनुकूलित किया जा सकता है, और कुछ मापनीय सुधार प्रदान करेगा। लेकिन देखो कि सरल अनुकूलन प्राप्त करने के लिए क्या लिया गया - कोड की लगभग 50 पंक्तियां। कोड की उन पंक्तियों पर लागत आती है, खासकर जब वे जावा लाइब्रेरी में शायद सबसे व्यापक रूप से उपयोग की जाने वाली कक्षा का हिस्सा हैं।लागत कई रूपों में आता:

  • संसाधन - यही कारण है कि कोड है कि कुछ डेवलपर समय लेखन, परीक्षण, दस्तावेजीकरण खर्च करना चाहिए की 50 लाइनों है, और जावा भाषा के जीवन भर के लिए भी बनाए रखेगा।
  • जोखिम - प्रारंभिक परीक्षण से पहले सूक्ष्म बग के लिए यह 50 अवसर हैं।
  • जटिलता - यह कोड की 50 अतिरिक्त पंक्तियां हैं जो कोई भी डेवलपर जो समझना चाहता है कि विधि कैसे काम करती है उसे पढ़ने और समझने में समय लगता है।

आपका प्रश्न अब नीचे उबलता है "यह एक विधि एक विशेष मामले को संभालने के लिए अनुकूलित क्यों किया गया था, लेकिन दूसरा नहीं?" या इससे भी अधिक आम तौर पर "यह विशेष सुविधा क्यों लागू नहीं हुई थी?" कोई भी मूल लेखक निश्चित रूप से इसका उत्तर नहीं दे सकता है, लेकिन जवाब लगभग हमेशा होता है कि या तो उस सुविधा के लिए पर्याप्त मांग नहीं है, या यह सुविधा प्राप्त करने से प्राप्त लाभ को इसे जोड़ने की लागत के लायक नहीं माना जाता है।

+0

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

+0

@marcolopes कृपया मेरा संपादित उत्तर देखें - आपका प्रश्न अब बहुत अलग है विभाजन विधि की तुलना में शामिल है। – Alex

+0

आपका उत्तर निर्णायक है। मैं इसे स्वीकार करूंगा। मेरा मानना ​​है कि प्रश्न शीर्षक में बदलाव क्रम में है। – marcolopes