यदि प्रदर्शन महत्वपूर्ण है, तो क्या मुझे जावा के String.format () का उपयोग करना चाहिए?


215

हमें लॉग आउटपुट आदि के लिए हर समय स्ट्रिंग्स का निर्माण करना होगा। JDK संस्करणों पर हमने सीखा है कि कब StringBuffer(कई एपेंड, थ्रेड सेफ) और StringBuilder(कई एपेंड, नॉन थ्रेड-सेफ) का उपयोग करना है।

उपयोग करने की सलाह क्या है String.format()? क्या यह कुशल है, या क्या हमें वन-लाइनर्स के लिए सहमति के साथ रहने के लिए मजबूर होना चाहिए जहां प्रदर्शन महत्वपूर्ण है?

उदाहरण के लिए बदसूरत पुरानी शैली,

String s = "What do you get if you multiply " + varSix + " by " + varNine + "?";

बनाम नई शैली (String.format, जो संभवतः धीमी है),

String s = String.format("What do you get if you multiply %d by %d?", varSix, varNine);

नोट: मेरा विशिष्ट उपयोग मामला मेरे पूरे कोड में सैकड़ों 'वन-लाइनर' लॉग स्ट्रिंग्स का है। वे एक लूप शामिल नहीं करते हैं, इसलिए StringBuilderबहुत भारी है। मुझे String.format()विशेष रूप से दिलचस्पी है ।


28
आप इसका परीक्षण क्यों नहीं करते?
एड एस।

1
यदि आप इस आउटपुट का उत्पादन कर रहे हैं, तो मुझे लगता है कि इसे मानव द्वारा पठनीय होना चाहिए क्योंकि एक मानव इसे पढ़ सकता है। सबसे अधिक 10 लाइन प्रति सेकेंड कहते हैं। मुझे लगता है कि आप पाएंगे कि यह वास्तव में कोई फर्क नहीं पड़ता कि आप किस दृष्टिकोण को लेते हैं, यदि यह उल्लेखनीय रूप से धीमा है, तो उपयोगकर्ता इसकी सराहना कर सकता है। ;) तो नहीं, StringBuilder ज्यादातर स्थितियों में भारी नहीं है।
पीटर लॉरी

9
@Peter, नहीं, यह मनुष्यों द्वारा वास्तविक समय में पढ़ने के लिए बिल्कुल नहीं है! यह विश्लेषण में मदद करने के लिए है जब चीजें गलत हो जाती हैं। लॉग आउटपुट आमतौर पर प्रति सेकंड हजारों लाइनें होगी, इसलिए इसे कुशल बनाने की आवश्यकता है।
वायुसेना

5
यदि आप प्रति सेकंड कई हज़ारों पंक्तियों का उत्पादन कर रहे हैं, तो मैं सुझाव दूंगा कि 1) छोटे पाठ का उपयोग करें, यहां तक ​​कि कोई भी पाठ जैसे कि सादे सीएसवी, या बाइनरी 2) स्ट्रिंग का उपयोग बिल्कुल न करें, आप डेटा को बाइटबफ़र में बनाए बिना लिख ​​सकते हैं किसी भी वस्तु (पाठ या बाइनरी के रूप में) 3) डिस्क या सॉकेट में डेटा लिखने की पृष्ठभूमि। आपको प्रति सेकंड लगभग 1 मिलियन लाइनों को बनाए रखने में सक्षम होना चाहिए। (मूल रूप से जितना आपके डिस्क सबसिस्टम की अनुमति होगी) आप इसे 10x के फटने को प्राप्त कर सकते हैं।
पीटर लॉरी

7
यह सामान्य मामले के लिए प्रासंगिक नहीं है, लेकिन विशेष रूप से लॉगिंग के लिए, LogBack (मूल Log4j लेखक द्वारा लिखित) के पास एक प्रकार का पैरामीटर लॉगिंग है जो इस सटीक समस्या को संबोधित करता है - logback.qos.ch/manual/altecture/#ParametrizedLogging
मैट पासेल

जवाबों:


123

मैंने परीक्षण करने के लिए एक छोटी कक्षा लिखी जिसमें दो का बेहतर प्रदर्शन है और + प्रारूप से आगे आता है। ५ से ६.६ तक एक कारक द्वारा इसे स्वयं करने का प्रयास करें

import java.io.*;
import java.util.Date;

public class StringTest{

    public static void main( String[] args ){
    int i = 0;
    long prev_time = System.currentTimeMillis();
    long time;

    for( i = 0; i< 100000; i++){
        String s = "Blah" + i + "Blah";
    }
    time = System.currentTimeMillis() - prev_time;

    System.out.println("Time after for loop " + time);

    prev_time = System.currentTimeMillis();
    for( i = 0; i<100000; i++){
        String s = String.format("Blah %d Blah", i);
    }
    time = System.currentTimeMillis() - prev_time;
    System.out.println("Time after for loop " + time);

    }
}

विभिन्न एन के लिए उपरोक्त भाग से पता चलता है कि दोनों रैखिक व्यवहार करते हैं, लेकिन String.format5-30 गुना धीमा है।

कारण यह है कि वर्तमान कार्यान्वयन में String.formatपहले इनपुट को नियमित अभिव्यक्तियों के साथ जोड़ा जाता है और फिर मापदंडों में भर जाता है। दूसरी ओर, इसके साथ संबंध, जेवैक (जेआईटी द्वारा नहीं) द्वारा अनुकूलित हो जाता है और StringBuilder.appendसीधे उपयोग करता है।

रनटाइम तुलना


12
इस परीक्षण में एक दोष यह है कि यह पूरी तरह से सभी स्ट्रिंग प्रारूपण का अच्छा प्रतिनिधित्व नहीं है। अक्सर इसमें तर्क शामिल होते हैं कि क्या शामिल करें और विशिष्ट मानों को स्ट्रिंग्स में स्वरूपित करने के लिए तर्क। किसी भी वास्तविक परीक्षा को वास्तविक दुनिया के परिदृश्यों को देखना चाहिए।
ओरियन एड्रियन

9
SO + के बारे में SO पर एक और सवाल था। छंद StringBuffer, जावा के हालिया संस्करणों में + को StringBuffer से बदल दिया गया था जब संभव हो तो प्रदर्शन अलग नहीं होगा
hhafez

25
यह बहुत सारे माइक्रोबैन्चमार्क की तरह दिखता है जिसे बहुत ही बेकार तरीके से अनुकूलित किया जा रहा है।
डेविड एच।

20
एक और खराब कार्यान्वित माइक्रो-बेंचमार्क। दोनों विधियाँ परिमाण के क्रम से कैसे मापी जाती हैं। कैसे उपयोग करने के बारे में, 100, 1000, 10000, 1000000, संचालन। यदि आप केवल एक परीक्षण, परिमाण के एक क्रम पर, एक ऐसे अनुप्रयोग पर, जो एक अलग कोर पर नहीं चल रहा है; संदर्भ स्विचिंग, पृष्ठभूमि प्रक्रियाओं, आदि के कारण अंतर के no साइड-इफेक्ट्स ’के रूप में कितना अंतर लिखा जा सकता है, यह बताने का कोई तरीका नहीं है
इवान प्लाइस

8
इसके अलावा जब तक आप मुख्य JIT से बाहर नहीं निकलते हैं, तब तक किक नहीं मार सकते।
Jan Zyka

241

मैंने hhafez कोड लिया और मेमोरी टेस्ट जोड़ा :

private static void test() {
    Runtime runtime = Runtime.getRuntime();
    long memory;
    ...
    memory = runtime.freeMemory();
    // for loop code
    memory = memory-runtime.freeMemory();

मैं इसे प्रत्येक दृष्टिकोण के लिए अलग से चलाता हूं, '+' ऑपरेटर, String.format और StringBuilder (कॉलिंग toString ()), इसलिए उपयोग की जाने वाली मेमोरी अन्य दृष्टिकोणों से प्रभावित नहीं होगी। मैंने और अधिक संयोजन जोड़े, जिससे "ब्लाह" + i + "ब्लाह" + i + "ब्लाह" + i + "ब्लाह" के रूप में स्ट्रिंग बना।

परिणाम निम्नानुसार हैं ( प्रत्येक के औसतन 5 रन):
दृष्टिकोण समय (एमएस) मेमोरी आवंटित (लंबी)
'+' ऑपरेटर 747 320,504
String.format 16484 373,312
StringBuilder 769 57,344

हम देख सकते हैं कि स्ट्रिंग '+' और स्ट्रिंगब्यूलर व्यावहारिक रूप से समान समय-वार हैं, लेकिन स्ट्रिंगब्यूरी मेमोरी उपयोग में अधिक कुशल है। यह बहुत महत्वपूर्ण है जब हमारे पास एक समय अंतराल में कई लॉग कॉल (या स्ट्रिंग्स को शामिल करने वाला कोई अन्य स्टेटमेंट) होता है, ताकि गारबेज कलेक्टर को '+' ऑपरेटर के परिणामस्वरूप कई स्ट्रिंग इंस्टेंसेस को साफ़ करने के लिए नहीं मिलेगा।

और एक नोट, बीटीडब्ल्यू, लॉगिंग की जांच करना न भूलें संदेश निर्माण से पहले स्तर की

निष्कर्ष:

  1. मैं StringBuilder का उपयोग कर रहा हूँ।
  2. मेरे पास बहुत समय या बहुत कम जीवन है।

8
"संदेश का निर्माण करने से पहले लॉगिंग स्तर की जांच करना न भूलें", एक अच्छी सलाह है, यह कम से कम डिबग संदेशों के लिए किया जाना चाहिए, क्योंकि उनमें से बहुत कुछ हो सकता है और उन्हें उत्पादन में सक्षम नहीं होना चाहिए।
स्टिव्लो

39
नहीं, यह सही नहीं है। क्षमा करें, लेकिन कुंद होने की संख्या ने इसे आकर्षित किया है, यह चिंताजनक नहीं है। +ऑपरेटर का उपयोग करना समकक्ष StringBuilderकोड के लिए संकलित करता है । इस तरह के माइक्रोबेनचर्च प्रदर्शन को मापने का एक अच्छा तरीका नहीं है - क्यों नहीं jvisualvm का उपयोग करें, यह एक कारण के लिए jdk में है। धीमी String.format() हो जाएगी, लेकिन किसी भी ऑब्जेक्ट आवंटन के बजाय प्रारूप स्ट्रिंग को पार्स करने के लिए समय के कारण। कलाकृतियों प्रवेश करने के सृजन टाल जब तक आप सुनिश्चित करें कि वे जरूरत हो रहे है अच्छी सलाह है, लेकिन अगर यह एक प्रदर्शन प्रभाव होने की जाएगी यह गलत जगह पर है।
कर्टनडॉग

1
@CurtainDog, आपकी टिप्पणी चार साल पुरानी पोस्ट पर की गई थी, क्या आप दस्तावेज़ को इंगित कर सकते हैं या अंतर को संबोधित करने के लिए एक अलग उत्तर बना सकते हैं?
कुर्टज़बॉट

1
: के @ CurtainDog की टिप्पणी समर्थन में संदर्भ stackoverflow.com/a/1532499/2872712 । जब तक कि लूप में नहीं किया जाता है, तब तक इसे पसंद किया जाता है।
खूबानी

And a note, BTW, don't forget to check the logging level before constructing the message.अच्छी सलाह नहीं है। यह मानते हुए कि हम java.util.logging.*विशेष रूप से बात कर रहे हैं , लॉगिंग स्तर की जांच तब कर रहे हैं जब आप उन्नत प्रसंस्करण करने के बारे में बात कर रहे हैं जो एक प्रोग्राम पर प्रतिकूल प्रभाव पैदा करेगा जो कि आप नहीं चाहेंगे कि जब प्रोग्राम लॉगिंग न हो तो वह उचित स्तर पर चालू हो। स्ट्रिंग स्वरूपण उस प्रकार का संसाधन नहीं है जो सभी पर होता है। फ़ॉर्मेटिंग java.util.loggingफ्रेमवर्क का हिस्सा है , और लकड़हारा पहले कभी लॉग इन करने से पहले लॉगिंग स्तर की जाँच करता है।
searchengine27

30

यहां प्रस्तुत सभी बेंचमार्क में कुछ खामियां हैं , इस प्रकार परिणाम विश्वसनीय नहीं हैं।

मुझे आश्चर्य हुआ कि बेंचमार्किंग के लिए किसी ने भी जेएमएच का इस्तेमाल नहीं किया , इसलिए मैंने किया।

परिणाम:

Benchmark             Mode  Cnt     Score     Error  Units
MyBenchmark.testOld  thrpt   20  9645.834 ± 238.165  ops/s  // using +
MyBenchmark.testNew  thrpt   20   429.898 ±  10.551  ops/s  // using String.format

इकाइयाँ प्रति सेकंड ऑपरेशन हैं, जितना बेहतर होगा। बेंचमार्क स्रोत कोड । OpenJDK IcedTea 2.5.4 जावा वर्चुअल मशीन का उपयोग किया गया था।

तो, पुरानी शैली (+ का उपयोग करके) बहुत तेज है।


5
यह व्याख्या करना बहुत आसान होगा यदि आपने एनोटेट किया था जो "+" था और जो "प्रारूप" था।
AjahnCharles 15

21

आपकी पुरानी बदसूरत शैली JAVAC 1.6 द्वारा स्वचालित रूप से संकलित की गई है:

StringBuilder sb = new StringBuilder("What do you get if you multiply ");
sb.append(varSix);
sb.append(" by ");
sb.append(varNine);
sb.append("?");
String s =  sb.toString();

तो इस और StringBuilder का उपयोग करने के बीच बिल्कुल कोई अंतर नहीं है।

String.format एक बहुत अधिक हैवीवेट है क्योंकि यह एक नया फॉर्मैटर बनाता है, आपके इनपुट फॉर्मेट स्ट्रिंग को पार्स करता है, एक स्ट्रिंगबर्स्ट बनाता है, इसे सबकुछ जोड़ देता है और स्टर्लिंग () को कॉल करता है।


पठनीयता के संदर्भ में, आपके द्वारा पोस्ट किया गया कोड बहुत अधिक है ... String.format की तुलना में बोझिल ("यदि आप% d से% d गुणा करें तो आपको क्या मिलेगा?", VarSix, varNine);
दशहरा

12
+और StringBuilderवास्तव में कोई अंतर नहीं है। दुर्भाग्य से इस धागे में अन्य उत्तरों में बहुत गलत जानकारी है। मुझे प्रश्न को बदलने के लिए लगभग लुभाया गया है how should I not be measuring performance
पर्देटडॉग

12

जावा का String.format ऐसा काम करता है:

  1. यह प्रारूप स्ट्रिंग की सूची में विस्फोट करके, प्रारूप स्ट्रिंग को पार्स करता है
  2. यह प्रारूप विखंड को पुनरावृत्त करता है, एक स्ट्रिंगबर्ल में प्रस्तुत करता है, जो मूल रूप से एक सरणी है जो एक नए सरणी में कॉपी करके अपने आप को आवश्यक बनाता है। यह आवश्यक है क्योंकि हम अभी तक नहीं जानते कि अंतिम स्ट्रिंग को आवंटित करने के लिए कितना बड़ा है
  3. StringBuilder.toString () अपने आंतरिक बफर को एक नए स्ट्रिंग में कॉपी करता है

यदि इस डेटा के लिए अंतिम गंतव्य एक स्ट्रीम है (जैसे किसी वेबपृष्ठ को रेंडर करना या किसी फ़ाइल को लिखना), तो आप सीधे प्रारूप को अपनी स्ट्रीम में इकट्ठा कर सकते हैं:

new PrintStream(outputStream, autoFlush, encoding).format("hello {0}", "world");

मैं अनुमान लगाता हूं कि ऑप्टिमाइज़र प्रारूप स्ट्रिंग प्रसंस्करण को दूर कर देगा। यदि ऐसा है, तो आप अपने String.format को StringBuilder में मैन्युअल रूप से नियंत्रित करने के लिए बराबर परिशोधित प्रदर्शन से बचे हैं ।


5
मुझे नहीं लगता कि प्रारूप स्ट्रिंग प्रसंस्करण के अनुकूलन के बारे में आपकी अटकलें सही हैं। जावा 7 का उपयोग करते हुए कुछ वास्तविक दुनिया के परीक्षणों में, मैंने पाया कि String.formatआंतरिक छोरों (लाखों बार चलने) के परिणामस्वरूप मेरे निष्पादन समय का 10% से अधिक खर्च हुआ java.util.Formatter.parse(String)। यह इंगित करता है कि आंतरिक छोरों में, आपको कॉल करने Formatter.formatया कुछ भी करने से बचना चाहिए जो इसे कॉल करता है, जिसमें PrintStream.format(जावा के मानक काम में दोष, IMO, खासकर जब से आप पार्स किए गए प्रारूप स्ट्रिंग को कैश नहीं कर सकते)।
एंडी मैककिनले

8

उपरोक्त पहले उत्तर पर विस्तार / सही करने के लिए, यह अनुवाद नहीं है कि String.format वास्तव में मदद करेगा।
जब आप किसी दिनांक / समय (या एक संख्यात्मक प्रारूप, आदि) को मुद्रित कर रहे हैं, तो String.format क्या मदद करेगा, जहाँ स्थानीयकरण (l10n) अंतर हैं (यानी, कुछ देश 04Feb2009 को प्रिंट करेंगे और अन्य लोग Feb042009 को प्रिंट करेंगे)।
अनुवाद के साथ, आप किसी भी बाहरी स्ट्रिंग्स (जैसे त्रुटि संदेश और क्या नहीं) को एक संपत्ति बंडल में स्थानांतरित करने के बारे में बात कर रहे हैं ताकि आप संसाधन भाषा और मैसेजफ़ॉर्मैट का उपयोग करके सही भाषा के लिए सही बंडल का उपयोग कर सकें।

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


1
मैं कहता हूँ कि प्रदर्शन-वार, String.format बनाम सादा समवशरण क्या आप इसे पसंद करते हैं, मुझे लगता है कि यह गलत है। प्रदर्शन के लिहाज से, कॉनसेप्ट बेहतर है। अधिक जानकारी के लिए कृपया मेरे उत्तर पर एक नज़र डालें।
एडम स्टेलमस्ज़ेन्क

6

आपके उदाहरण में, प्रदर्शन बहुत अधिक भिन्न नहीं है, लेकिन विचार करने के लिए अन्य मुद्दे हैं: अर्थात् स्मृति विखंडन। यहां तक ​​कि समवर्ती ऑपरेशन एक नया स्ट्रिंग बना रहा है, भले ही इसका अस्थायी (जीसी यह समय लगता है और यह अधिक काम है)। String.format () केवल अधिक पठनीय है और इसमें कम विखंडन शामिल है।

इसके अलावा, यदि आप किसी विशेष प्रारूप का उपयोग कर रहे हैं, तो यह मत भूलिए कि आप सीधे फ़ॉर्मेटर () वर्ग का उपयोग कर सकते हैं (सभी String.format () करता है कि एक उपयोग फ़ॉर्मैटर इंस्टेंस को त्वरित करना है)।

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

String getSmallString() {
  String largeString = // load from file; say 2M in size
  return largeString.substring(100, 300);
}

यह बड़ा तार अभी भी स्मृति में है क्योंकि यह जावा सब्सट्रिंग्स कैसे काम करता है। एक बेहतर संस्करण है:

  return new String(largeString.substring(100, 300));

या

  return String.format("%s", largeString.substring(100, 300));

यदि आप एक ही समय में अन्य सामान कर रहे हैं तो दूसरा रूप संभवतः अधिक उपयोगी है।


8
"संबंधित प्रश्न" को इंगित करने वाला मूल्य वास्तव में C # है और इसलिए लागू नहीं है।
एयर

स्मृति विखंडन को मापने के लिए आपने किस उपकरण का उपयोग किया था और क्या विखंडन भी राम के लिए गति का अंतर बनाता है?
कृतजितराजी

यह इंगित करने योग्य है कि प्रतिस्थापन विधि जावा 7 + से बदल दी गई थी। अब इसे एक नया स्ट्रिंग प्रतिनिधित्व लौटाया जाना चाहिए जिसमें केवल प्रतिस्थापित अक्षर हैं। इसका मतलब है कि
कॉलिंग

5

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

अब यदि आप कभी भी कुछ भी अनुवाद करने की योजना नहीं बनाते हैं, तो या तो जावा में निर्मित + ऑपरेटरों के रूपांतरण में भरोसा करें StringBuilder। या जावा का StringBuilderस्पष्ट रूप से उपयोग करें ।


3

केवल लॉगिंग पॉइंट से एक और परिप्रेक्ष्य।

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

मुझे लगता है कि सूत्रण का उपयोग करके लॉगिंग की प्रेरणा स्ट्रिंग संघनन से बचने से आती है। मूल रूप से, यदि आप इसे लॉग इन नहीं करने जा रहे हैं, तो आप स्ट्रिंग कॉनट का ओवरहेड नहीं करना चाहते हैं।

जब तक आप लॉग इन नहीं करना चाहते हैं, तब तक आपको वास्तव में संक्षिप्त / प्रारूप की आवश्यकता नहीं है। अगर मैं इस तरह से एक विधि को परिभाषित करता हूं तो बताएं

public void logDebug(String... args, Throwable t) {
    if(debugOn) {
       // call concat methods for all args
       //log the final debug message
    }
}

इस दृष्टिकोण में कैनकैट / फॉर्मैटर को वास्तव में नहीं बुलाया जाता है यदि उसका डिबग संदेश और डिबगऑन = गलत है

हालांकि यहां फॉर्मैटर के बजाय स्ट्रिंगबर्ल का उपयोग करना बेहतर होगा। मुख्य प्रेरणा से किसी से बचने के लिए है।

उसी समय से मुझे जोड़ना पसंद नहीं है "यदि" प्रत्येक लॉगिंग स्टेटमेंट के लिए ब्लॉक करता है

  • यह पठनीयता को प्रभावित करता है
  • मेरी इकाई परीक्षणों पर कवरेज कम कर देता है - जब आप यह सुनिश्चित करना चाहते हैं कि हर पंक्ति का परीक्षण किया जाए, तो यह भ्रमित करता है।

इसलिए मैं ऊपर दिए गए तरीकों के साथ एक लॉगिंग यूटिलिटी क्लास बनाना पसंद करता हूं और प्रदर्शन हिट और इससे संबंधित किसी भी अन्य मुद्दों की चिंता किए बिना हर जगह इसका उपयोग करता हूं।


क्या आप slf4j-api जैसी मौजूदा लाइब्रेरी का लाभ उठा सकते हैं जो इस पैरामीटर को उनके पैरामीटर किए गए लॉगिंग फ़ीचर से संबोधित करता है? slf4j.org/faq.html#log_performance
ammianus

2

मैंने StringBuilder को शामिल करने के लिए सिर्फ hhafez के परीक्षण को संशोधित किया। StringBuilder XP पर jdk 1.6.0_10 क्लाइंट का उपयोग करके String.format की तुलना में 33 गुना तेज है। -Server स्विच का उपयोग करने से कारक 20 तक कम हो जाता है।

public class StringTest {

   public static void main( String[] args ) {
      test();
      test();
   }

   private static void test() {
      int i = 0;
      long prev_time = System.currentTimeMillis();
      long time;

      for ( i = 0; i < 1000000; i++ ) {
         String s = "Blah" + i + "Blah";
      }
      time = System.currentTimeMillis() - prev_time;

      System.out.println("Time after for loop " + time);

      prev_time = System.currentTimeMillis();
      for ( i = 0; i < 1000000; i++ ) {
         String s = String.format("Blah %d Blah", i);
      }
      time = System.currentTimeMillis() - prev_time;
      System.out.println("Time after for loop " + time);

      prev_time = System.currentTimeMillis();
      for ( i = 0; i < 1000000; i++ ) {
         new StringBuilder("Blah").append(i).append("Blah");
      }
      time = System.currentTimeMillis() - prev_time;
      System.out.println("Time after for loop " + time);
   }
}

हालांकि यह कठोर लग सकता है, मैं इसे केवल दुर्लभ मामलों में प्रासंगिक मानता हूं, क्योंकि निरपेक्ष संख्या बहुत कम है: 1 मिलियन के लिए 4 s सरल String.format कॉल ठीक है - जब तक मैं उन्हें लॉगिंग या ऑडियो के लिए उपयोग करता हूं पसंद।

अद्यतन: जैसा कि टिप्पणियों में sjbotha द्वारा बताया गया है, StringBuilder परीक्षण अमान्य है, क्योंकि यह एक अंतिम याद आ रही है .toString()

से सही गति-अप कारक String.format(.)के लिए StringBuilder23 मेरी मशीन (के साथ 16 पर है -serverस्विच)।


1
आपका परीक्षण अमान्य है क्योंकि यह केवल लूप होने के कारण खाए गए समय को ध्यान में रखने में विफल रहता है। आपको इसे शामिल करना चाहिए और इसे अन्य सभी परिणामों से घटाना चाहिए, कम से कम (हाँ यह एक महत्वपूर्ण प्रतिशत हो सकता है)।
cletus

मैंने ऐसा किया, लूप के लिए 0 मि। लेकिन भले ही इसमें समय लगे, लेकिन यह केवल कारक को बढ़ाएगा।
द डीकमैन

3
StringBuilder परीक्षण अमान्य है क्योंकि यह वास्तव में आपको जो String उपयोग कर सकता है उसे देने के लिए आखिर में String () को कॉल नहीं करता है। मैंने इसे जोड़ा और नतीजा यह है कि स्ट्रिंगबुलस्टल के रूप में + के रूप में उसी समय के बारे में लेता है। मुझे यकीन है कि जब आप अपीलों की संख्या बढ़ाएंगे तो यह अंततः सस्ता हो जाएगा।
सेरेल बोथा

1

यहाँ हाफ़ेज़ प्रविष्टि का संशोधित संस्करण है। इसमें स्ट्रिंग बिल्डर विकल्प शामिल है।

public class BLA
{
public static final String BLAH = "Blah ";
public static final String BLAH2 = " Blah";
public static final String BLAH3 = "Blah %d Blah";


public static void main(String[] args) {
    int i = 0;
    long prev_time = System.currentTimeMillis();
    long time;
    int numLoops = 1000000;

    for( i = 0; i< numLoops; i++){
        String s = BLAH + i + BLAH2;
    }
    time = System.currentTimeMillis() - prev_time;

    System.out.println("Time after for loop " + time);

    prev_time = System.currentTimeMillis();
    for( i = 0; i<numLoops; i++){
        String s = String.format(BLAH3, i);
    }
    time = System.currentTimeMillis() - prev_time;
    System.out.println("Time after for loop " + time);

    prev_time = System.currentTimeMillis();
    for( i = 0; i<numLoops; i++){
        StringBuilder sb = new StringBuilder();
        sb.append(BLAH);
        sb.append(i);
        sb.append(BLAH2);
        String s = sb.toString();
    }
    time = System.currentTimeMillis() - prev_time;
    System.out.println("Time after for loop " + time);

}

}

लूप के बाद का समय 391 लूप के बाद का समय 4163 लूप के बाद का समय लूप 227 के लिए


0

इसका उत्तर बहुत हद तक इस बात पर निर्भर करता है कि आपका विशिष्ट जावा कंपाइलर इसके द्वारा उत्पन्न बाइटकोड को कैसे अनुकूलित करता है। स्ट्रिंग्स अपरिवर्तनीय हैं और, सैद्धांतिक रूप से, प्रत्येक "+" ऑपरेशन एक नया बना सकता है। लेकिन, आपका कंपाइलर निश्चित रूप से लंबे स्ट्रिंग्स के निर्माण में अंतरिम कदमों का अनुकूलन करता है। यह पूरी तरह से संभव है कि ऊपर दिए गए कोड की दोनों पंक्तियाँ ठीक उसी बायोटेक को उत्पन्न करती हैं।

पता करने का एकमात्र वास्तविक तरीका कोड को आपके वर्तमान परिवेश में पुनरावृत्त करना है। एक QD ऐप लिखें जो स्ट्रिंग्स को दोनों तरीकों से पुनरावृत्त करता है और देखें कि वे एक दूसरे के खिलाफ कैसे समय निकालते हैं।


1
दूसरा उदाहरण के लिए bytecode निश्चित रूप से String.format कहता है, लेकिन अगर एक सरल संघात किया तो मैं भयभीत होऊंगा। कंपाइलर एक प्रारूप स्ट्रिंग का उपयोग क्यों करेगा, जिसे तब पार्स किया जाना होगा?
जॉन स्कीट

मैंने "बाइटकोड" का उपयोग किया जहां मुझे "बाइनरी कोड" कहना चाहिए था। जब यह सब jmps और movs के लिए नीचे आता है, यह अच्छी तरह से एक ही कोड हो सकता है।
हाँ - वह जेक।

0

"hello".concat( "world!" )संघनन में छोटी संख्या में तारों का उपयोग करने पर विचार करें । यह अन्य तरीकों की तुलना में प्रदर्शन के लिए और भी बेहतर हो सकता है।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.