Java'da toString () 'de StringBuilder vs String bitiştirmesi


925

toString()Aşağıdaki 2 uygulama göz önüne alındığında , hangisinin tercih edildiği:

public String toString(){
    return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}

veya

public String toString(){
    StringBuilder sb = new StringBuilder(100);
    return sb.append("{a:").append(a)
          .append(", b:").append(b)
          .append(", c:").append(c)
          .append("}")
          .toString();
}

?

Daha da önemlisi, sadece 3 özelliğimiz olduğunda, fark yaratmayabilir, ancak hangi noktada +concat'tan hangi noktaya geçersiniz StringBuilder?


40
Hangi noktada StringBuilder'a geçersiniz? Belleği veya performansı etkilediğinde. Ya da ne zaman olabilir. Bunu gerçekten sadece bir kez birkaç ip için yapıyorsanız, endişelenmeyin. Ancak bunu tekrar tekrar yapacaksanız, StringBuilder kullanırken ölçülebilir bir fark görmelisiniz.
ewall

parametrede 100'ün ortalaması nedir?
Asif Mushtaq

2
@UnKnown 100, StringBuilder'in başlangıç ​​boyutudur
sıralı olmayan

@nonsequitor Maksimum karakter 100 olacak mı?
Asif Mushtaq

10
@Bilinmeyen sadece başlangıç ​​boyutunu bilmiyorsanız, uğraştığınız dizenin yaklaşık boyutunu biliyorsanız, StringBuilderönceden ne kadar boyut tahsis edeceğinizi söyleyebilirsiniz , aksi takdirde, alan biterse, bir boyut oluşturarak boyutu iki katına çıkarmanız gerekir. yeni char[]dizi daha sonra verileri kopyala - ki bu maliyetlidir. Boyutu vererek hile yapabilirsiniz ve daha sonra bu dizi oluşturma için gerek yoktur - eğer dize ~ 100 karakter uzunluğunda olacağını düşünüyorsanız o zaman StringBuilder bu boyuta ayarlayabilirsiniz ve asla dahili olarak genişletmek zorunda kalmayacaksınız.
sıralı olmayan

Yanıtlar:


964

Sürüm 1 tercih edilir çünkü daha kısadır ve derleyici aslında sürüm 2'ye dönüşür - hiçbir performans farkı yoktur.

Daha da önemlisi, fark etmeyebilecek sadece 3 özelliğimiz var, ancak hangi noktada concat'tan builder'a geçiyorsunuz?

Bir döngüde bitiştirdiğiniz noktada - genellikle derleyici StringBuilderkendi başına değiştirilemez.


19
Bu doğrudur, ancak dil referansı bunun isteğe bağlı olduğunu da belirtir. Aslında, JRE 1.6.0_15 ile basit bir test yaptım ve ayrıştırılmış sınıfta herhangi bir derleyici optimizasyonu görmedim.
bruno conde

37
Ben sadece (JDK 1.6.0_16 üzerinde derlenmiş) sorudan kodu denedim ve beklendiği gibi optimizasyon bulundu. Eminim tüm modern derleyiciler bunu yapacak.
Michael Borgwardt

22
Haklısın. Bytecode bakarak açıkça StringBuilder optimizasyonu görebilirsiniz. Bir decompiler kullanıyordum ve bir şekilde nasıl concat'a dönüşüyor. +1
bruno conde

80
Ölü atı atmak için değil, spesifikasyondaki ifadeler: To increase the performance of repeated string concatenation, a Java compiler _may_ use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.Oradaki anahtar kelime olabilir . Bunun resmi olarak isteğe bağlı olduğu göz önüne alındığında (büyük olasılıkla uygulanmış olsa da) kendimizi korumamalıyız?
Lucas

93
@Lucas: Hayır, yapmamalıyız. Derleyici bu optimizasyonu gerçekleştirmemeye karar verirse, buna değmeyeceği için olacaktır. Vakaların% 99'unda, derleyici hangi optimizasyonun buna değer olduğunu daha iyi bilir, bu nedenle temel kural olarak geliştiricinin müdahale etmemesi gerekir. Tabii ki, durumunuz olabilir diğer% 1 düşmek, ama bu sadece kıyaslama (dikkatli) tarafından kontrol edilebilir.
sleske

255

Önemli olan, tek bir yerde tek bir birleştirme yazıyor veya zaman içinde biriktiriyor olmanızdır.

Verdiğiniz örnek için, StringBuilder'ı açıkça kullanmanın bir anlamı yoktur. (İlk durumunuz için derlenmiş koda bakın.)

Ancak, örneğin bir döngü içinde dize oluşturuyorsanız, StringBuilder kullanın.

Açıklamak için, hugeArray öğesinin binlerce dize içerdiğini varsayarak, şöyle kodlayın:

...
String result = "";
for (String s : hugeArray) {
    result = result + s;
}

ile karşılaştırıldığında çok zaman ve hafıza israfı sağlar:

...
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
    sb.append(s);
}
String result = sb.toString();

3
Evet, StringBuilder'ın String nesnesini tekrar tekrar oluşturması gerekmez.
Olga

124
. Dammit Ben 11 saniye vs 6.51min çalışıyorum büyük bir dize sınamak için bu 2 fonksiyonu kullanılır
user1722791

1
Bu arada da kullanabilirsiniz result += s;(ilk örnekte)
RAnders00

1
Bu ifade ile kaç tane nesne oluşturulacak? "{a:"+ a + ", b:" + b + ", c: " + c +"}";
Asif Mushtaq

1
Peki ya benzer: String str = (a == null)? null: a '+ (b == null)? null: b '+ (c == null)? c: c '+ ...; ? Bu optimizasyonun gerçekleşmesini engelleyecek mi?
amitfr

75

Tercih ederim:

String.format( "{a: %s, b: %s, c: %s}", a, b, c );

... çünkü kısa ve okunabilir.

Ben ediyorum değil sen çok yüksek tekrarlama sayısı ile bir döngü içinde kullanmak sürece hız için bu optimize ve performans farkını ölçmüştür.

Katılıyorum, eğer çok fazla parametre çıkarmanız gerekiyorsa, bu form kafa karıştırıcı olabilir (yorumlardan birinin söylediği gibi). Bu durumda, daha okunabilir bir forma geçebilirdim (belki de mat b'nin cevabından alınan apache-commons ToStringBuilder kullanarak ) ve performansı tekrar görmezden gelirim .


64
Aslında daha uzun, daha fazla sembol içeriyor ve değişkenleri bir metnin sıra dışı.
Tom Hawtin - tackline

4
Yani diğer yaklaşımlardan birinden daha az okunabilir olduğunu söyleyebilir misiniz?
tangens

3
Bunu yazmayı tercih ediyorum, çünkü daha fazla değişken eklemek daha kolay, ancak daha okunabilir olduğundan emin değilim - özellikle de argümanların sayısı arttıkça. Farklı zamanlarda bit eklemeniz gerektiğinde de çalışmaz.
Alex Feinman

78
Okumak daha zor görünüyor (benim için). Şimdi {...} ve parametreler arasında ileri geri tarama yapmam gerekiyor.
Steve Kuo

10
Bu formu tercih ederim, çünkü parametrelerden biri güvenli isenull
rds

74

Çoğu durumda, iki yaklaşım arasında gerçek bir fark görmezsiniz, ancak bunun gibi en kötü bir senaryo oluşturmak kolaydır:

public class Main
{
    public static void main(String[] args)
    {
        long now = System.currentTimeMillis();
        slow();
        System.out.println("slow elapsed " + (System.currentTimeMillis() - now) + " ms");

        now = System.currentTimeMillis();
        fast();
        System.out.println("fast elapsed " + (System.currentTimeMillis() - now) + " ms");
    }

    private static void fast()
    {
        StringBuilder s = new StringBuilder();
        for(int i=0;i<100000;i++)
            s.append("*");      
    }

    private static void slow()
    {
        String s = "";
        for(int i=0;i<100000;i++)
            s+="*";
    }
}

Çıktı:

slow elapsed 11741 ms
fast elapsed 7 ms

Sorun, bir dizeye + = eklemek, yeni bir dizeyi yeniden yapılandırmasıdır, bu nedenle dizelerinizin uzunluğuna (her ikisinin toplamı) doğrusal bir maliyete neden olur.

Yani - sorunuza:

İkinci yaklaşım daha hızlı olurdu, ancak daha az okunabilir ve bakımı daha zordur. Söylediğim gibi, özel durumunuzda muhtemelen farkı görmezsiniz.


.Concat () 'i unutmayın. Ben 10 ila 18 ms herhangi bir yerde olmak için geçen zaman tahmin ediyorum orijinal sonrası örneği gibi kısa dizeleri kullanırken ihmal edilebilir hale.
Droo

9
Haklı olsanız da +=, orijinal örnek, +derleyicinin tek birstring.concat çağrıya diziydi. Sonuçlarınız geçerli değil.
Blindy

1
@Blindy & Droo: - Siz ikiniz haklısınız. Böyle bir senaryoda .concate kullanmak en iyi çözümdür, çünkü + = döngü rutini her çalıştırıldığında yeni nesne oluşturur.
perilbrain

3
onun toString () işlevinin bir döngü içinde çağrılmadığını biliyor musunuz?
Omry Yadan

Hızı test etmek için bu örneği denedim. Yani benim sonuçlarım: yavaş geçen 29672 ms; hızlı geçen 15 ms. Yani cevap açıktır. Ama 100 yineleme olurdu - zaman aynı - 0 ms. 500 yineleme - 16 ms ve 0 ms. Ve bunun gibi.
Ernestas Gruodis

28

Ayrıca, append veya + kullanması konusunda patronumla çatıştım. Append'i kullandıkları için (her yeni nesne oluşturulduğunda söyledikleri gibi hala anlayamıyorum). Michael Borgwardt'ın açıklamasını sevmeme rağmen sadece gelecekte gerçekten bilmesi gereken bir açıklama yapmak istedim.

/**
 *
 * @author Perilbrain
 */
public class Appc {
    public Appc() {
        String x = "no name";
        x += "I have Added a name" + "We May need few more names" + Appc.this;
        x.concat(x);
        // x+=x.toString(); --It creates new StringBuilder object before concatenation so avoid if possible
        //System.out.println(x);
    }

    public void Sb() {
        StringBuilder sbb = new StringBuilder("no name");
        sbb.append("I have Added a name");
        sbb.append("We May need few more names");
        sbb.append(Appc.this);
        sbb.append(sbb.toString());
        // System.out.println(sbb.toString());
    }
}

ve yukarıdaki sınıfın sökülmesi şu şekilde ortaya çıkar:

 .method public <init>()V //public Appc()
  .limit stack 2
  .limit locals 2
met001_begin:                                  ; DATA XREF: met001_slot000i
  .line 12
    aload_0 ; met001_slot000
    invokespecial java/lang/Object.<init>()V
  .line 13
    ldc "no name"
    astore_1 ; met001_slot001
  .line 14

met001_7:                                      ; DATA XREF: met001_slot001i
    new java/lang/StringBuilder //1st object of SB
    dup
    invokespecial java/lang/StringBuilder.<init>()V
    aload_1 ; met001_slot001
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    ldc "I have Added a nameWe May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    aload_0 ; met001_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    astore_1 ; met001_slot001
  .line 15
    aload_1 ; met001_slot001
    aload_1 ; met001_slot001
    invokevirtual java/lang/String.concat(Ljava/lang/String;)Ljava/lang/Strin\
g;
    pop
  .line 18
    return //no more SB created
met001_end:                                    ; DATA XREF: met001_slot000i ...

; ===========================================================================

;met001_slot000                                ; DATA XREF: <init>r ...
    .var 0 is this LAppc; from met001_begin to met001_end
;met001_slot001                                ; DATA XREF: <init>+6w ...
    .var 1 is x Ljava/lang/String; from met001_7 to met001_end
  .end method
;44-1=44
; ---------------------------------------------------------------------------


; Segment type: Pure code
  .method public Sb()V //public void Sb
  .limit stack 3
  .limit locals 2
met002_begin:                                  ; DATA XREF: met002_slot000i
  .line 21
    new java/lang/StringBuilder
    dup
    ldc "no name"
    invokespecial java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    astore_1 ; met002_slot001
  .line 22

met002_10:                                     ; DATA XREF: met002_slot001i
    aload_1 ; met002_slot001
    ldc "I have Added a name"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 23
    aload_1 ; met002_slot001
    ldc "We May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 24
    aload_1 ; met002_slot001
    aload_0 ; met002_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    pop
  .line 25
    aload_1 ; met002_slot001
    aload_1 ; met002_slot001
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 28
    return
met002_end:                                    ; DATA XREF: met002_slot000i ...


;met002_slot000                                ; DATA XREF: Sb+25r
    .var 0 is this LAppc; from met002_begin to met002_end
;met002_slot001                                ; DATA XREF: Sb+9w ...
    .var 1 is sbb Ljava/lang/StringBuilder; from met002_10 to met002_end
  .end method
;96-49=48
; ---------------------------------------------------------------------------

Yukarıdaki iki koddan Michael'ın haklı olduğunu görebilirsiniz.Her durumda sadece bir SB nesnesi oluşturulur.


27

Java 1.5'ten bu yana, "+" ve StringBuilder.append () ile basit bir satır birleştirmesi tamamen aynı bayt kodunu oluşturur.

Kod okunabilirliği için "+" kullanın.

2 istisna:

  • çok iş parçacıklı ortam: StringBuffer
  • döngülerde birleştirme: StringBuilder / StringBuffer

22

Java'nın (1.8) en son sürümünü kullanarak demontaj ( javap -c) derleyicinin getirdiği optimizasyonu gösterir. +ayrıca sb.append(), çok benzer bir kod oluşturur. Bununla birlikte, +bir for döngüsünde kullanıyorsak davranışı incelemek faydalı olacaktır .

For döngüsünde + kullanarak dizeler ekleme

Java:

public String myCatPlus(String[] vals) {
    String result = "";
    for (String val : vals) {
        result = result + val;
    }
    return result;
}

ByteCode :( fordöngü alıntısı)

12: iload         5
14: iload         4
16: if_icmpge     51
19: aload_3
20: iload         5
22: aaload
23: astore        6
25: new           #3                  // class java/lang/StringBuilder
28: dup
29: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
32: aload_2
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: aload         6
38: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
44: astore_2
45: iinc          5, 1
48: goto          12

Stringbuilder.append komutunu kullanarak dizeler ekleme

Java:

public String myCatSb(String[] vals) {
    StringBuilder sb = new StringBuilder();
    for(String val : vals) {
        sb.append(val);
    }
    return sb.toString();
}

ByteCdoe :( fordöngü alıntısı)

17: iload         5
19: iload         4
21: if_icmpge     43
24: aload_3
25: iload         5
27: aaload
28: astore        6
30: aload_2
31: aload         6
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: pop
37: iinc          5, 1
40: goto          17
43: aload_2

Yine de biraz göze batan fark var . İlk durumda, +kullanıldığında, StringBuilderdöngü yinelemesi için her biri için yeni oluşturulur ve oluşturulan sonuç bir toString()çağrı yapılarak saklanır (29 ila 41). Böylece +, fordöngüdeki operatörü kullanırken gerçekten ihtiyacınız olmayan ara Dizeler üretiyorsunuz .


1
Bu Oracle JDK veya OpenJDK mı?
Christophe Roussy

12

Java 9'da sürüm 1, invokedynamicçağrıya dönüştürüldüğünden daha hızlı olmalıdır . Daha fazla ayrıntı JEP-280'de bulunabilir :

Fikir, StringBuilder append dansı yerine java.lang.invoke.StringConcatFactory için birleştirme ihtiyacı olan değerleri kabul edecek basit bir invokeynamic çağrı ile değiştirmektir.


9

Performans nedeniyle, +=( Stringbirleştirme) kullanımı önerilmez. Nedeni: Java Stringdeğişmezdir, her yeni bir bitirme işleminde Stringyeni bir tane oluşturulur (yenisinin zaten String havuzunda olandan daha farklı bir parmak izi vardır ). Yeni dizeler oluşturmak GC'ye baskı yapar ve programı yavaşlatır: nesne oluşturma pahalıdır.

Aşağıdaki kod aynı zamanda daha pratik ve açık hale getirmelidir.

public static void main(String[] args) 
{
    // warming up
    for(int i = 0; i < 100; i++)
        RandomStringUtils.randomAlphanumeric(1024);
    final StringBuilder appender = new StringBuilder();
    for(int i = 0; i < 100; i++)
        appender.append(RandomStringUtils.randomAlphanumeric(i));

    // testing
    for(int i = 1; i <= 10000; i*=10)
        test(i);
}

public static void test(final int howMany) 
{
    List<String> samples = new ArrayList<>(howMany);
    for(int i = 0; i < howMany; i++)
        samples.add(RandomStringUtils.randomAlphabetic(128));

    final StringBuilder builder = new StringBuilder();
    long start = System.nanoTime();
    for(String sample: samples)
        builder.append(sample);
    builder.toString();
    long elapsed = System.nanoTime() - start;
    System.out.printf("builder - %d - elapsed: %dus\n", howMany, elapsed / 1000);

    String accumulator = "";
    start = System.nanoTime();
    for(String sample: samples)
        accumulator += sample;
    elapsed = System.nanoTime() - start;
    System.out.printf("concatenation - %d - elapsed: %dus\n", howMany, elapsed / (int) 1e3);

    start = System.nanoTime();
    String newOne = null;
    for(String sample: samples)
        newOne = new String(sample);
    elapsed = System.nanoTime() - start;
    System.out.printf("creation - %d - elapsed: %dus\n\n", howMany, elapsed / 1000);
}

Bir koşu için sonuçlar aşağıda bildirilmiştir.

builder - 1 - elapsed: 132us
concatenation - 1 - elapsed: 4us
creation - 1 - elapsed: 5us

builder - 10 - elapsed: 9us
concatenation - 10 - elapsed: 26us
creation - 10 - elapsed: 5us

builder - 100 - elapsed: 77us
concatenation - 100 - elapsed: 1669us
creation - 100 - elapsed: 43us

builder - 1000 - elapsed: 511us
concatenation - 1000 - elapsed: 111504us
creation - 1000 - elapsed: 282us

builder - 10000 - elapsed: 3364us 
concatenation - 10000 - elapsed: 5709793us
creation - 10000 - elapsed: 972us

1 birleştirme için sonuçlar dikkate alınmadığında (JIT henüz işini yapmıyordu), 10 birleştirme için bile performans cezası önemlidir; binlerce birleşim için fark çok büyük.

Bu çok hızlı deneyden öğrenilen dersler (yukarıdaki kodla kolayca tekrarlanabilir): +=birkaç birleştirmenin gerekli olduğu çok temel durumlarda bile, dizeleri birleştirmek için asla kullanmayın (dediğimiz gibi, yeni dizeler oluşturmak pahalıdır ve GC).


9

Aşağıdaki örneğe bakın:

static final int MAX_ITERATIONS = 50000;
static final int CALC_AVG_EVERY = 10000;

public static void main(String[] args) {
    printBytecodeVersion();
    printJavaVersion();
    case1();//str.concat
    case2();//+=
    case3();//StringBuilder
}

static void case1() {
    System.out.println("[str1.concat(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str = str.concat(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case2() {
    System.out.println("[str1+=str2]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str += UUID.randomUUID() + "---";
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case3() {
    System.out.println("[str1.append(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    StringBuilder str = new StringBuilder("");
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str.append(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");

}

static void saveTime(List<Long> executionTimes, long startTime) {
    executionTimes.add(System.currentTimeMillis() - startTime);
    if (executionTimes.size() % CALC_AVG_EVERY == 0) {
        out.println("average time for " + executionTimes.size() + " concatenations: "
                + NumberFormat.getInstance().format(executionTimes.stream().mapToLong(Long::longValue).average().orElseGet(() -> 0))
                + " ms avg");
        executionTimes.clear();
    }
}

Çıktı:

java bayt kodu sürümü: 8
java.version: 1.8.0_144
[str1.concat (str2)]
10000 bitiştirme için ortalama süre: 0.096 ms ortalama 10000 kez bitirme
süresi: 0.185 ms ortalama
ortalaması 10000 bitiştirme süresi: 0.327 ms
ortalama süre 10000 dizilimlerin: 0.501 ms avg
10000 concatenations ortalama süresi: 0.656 ms AVG
uzunlukta dizi oluşturuldu: 1950000 içinde 17745 ms
[dizge1 + = str2]
ortalama süre 10000 concatenations için: 0.21 ms avg
10000 concatenations ortalama süresi: 0.652 ms avg
için ortalama süre 10000 bağlantı: 1.129 ms ortalama
ortalama 10000 zaman sayısı: 1.727 ms avg
10000 birleştirme için ortalama süre: 2.302 ms avg
Oluşturulan uzunluk dizesi : 60279 ms'de 1950000
[str1.append (str2)]
10000 birleştirme için ortalama süre: 10000 birleştirme için 0.002 ms ortalama
ortalama süresi: 10000 birleştirme için 0.002 ms ortalama
ortalama süre: 10000 birleştirme için 0.002 ms ortalama
ortalama süre: 10000 birleştirme için 0.002 ms ortalama
ortalama süre: 0.002 ms avg
Oluşturulan dize uzunluğu: 1950000 in 100 ms

Dize uzunluğu arttıkça, birleştirme süresi de artar. Kesinlikle ihtiyaç duyulan
yer burasıdır StringBuilder.
Gördüğünüz gibi, birleştirme:UUID.randomUUID()+"---" zamanı gerçekten etkilemez.

Not: StringBuilder Java ne zaman kullanılacağını gerçekten sanmıyorum .
Bu soru toString(), çoğu zaman büyük dizelerin birleştirmelerini gerçekleştirmediğinden bahseder .


2019 Güncellemesi

O zamandan beri java8işler biraz değişti. Görünüşe göre (java13), birleştirme süresi +=pratik olarak aynıdır str.concat(). Ancak StringBuilderbirleştirme süresi hala sabittir . (Yukarıdaki orijinal yayın daha ayrıntılı çıktı eklemek için hafifçe düzenlendi)

java bayt kodu sürümü: 13
java.version: 13.0.1
[str1.concat (str2)]
10000 bitiştirme için ortalama süre: 0.047 ms
ortalama 10000 kez bitiştirme süresi: 0.1 ms ortalama
ortalama 10000 bitirim süresi: 0.17 ms ort
. 10000 dizilimlerin: 0.255 ms avg
10000 concatenations ortalama süresi: 0.336 ms AVG
uzunlukta dizi oluşturuldu: 1950000 içinde 9147 ms
[dizge1 + = str2]
ortalama süre 10000 concatenations için: 0.037 ms avg
10000 concatenations ortalama süresi: 0.097 ms avg
için ortalama süre 10000 bağlantı: 0.249 ms ortalama
ortalama 10000 zaman sayısı: 0.298 ms avg
10000 birleştirme için ortalama süre
: 0.326 ms avg Oluşturulan uzunluk dizesi: 1950000, 10191 ms'de
[str1.append (str2)]
10000 birleştirme için ortalama süre: 10000 birleştirme için 0.001 ms ortalama
ortalama süresi: 10000 birleştirme için 0.001 ms ortalama
ortalama süresi: 10000 birleştirme için 0.001 ms ortalama
ortalama süre: 10000 birleştirme için 0.001 ms ortalama
ortalama süre: 0.001 ms avg
Oluşturulan dize uzunluğu: 1950000 in 43 ms

Ayrıca, bytecode:8/java.version:13kombinasyonun,bytecode:8/java.version:8


Bu kabul edilen cevap olmalı .. concat veya StringBuilder seçimini belirleyen String Stream büyüklüğüne bağlıdır
user1428716

@ user1428716 FYI: yanıt java13şimdi farklı olan sonuçlarla güncellendi . Ama bence ana sonuç aynı kalıyor.
Marinos An

7

Apache Commons-Lang, kullanımı çok kolay olan bir ToStringBuilder sınıfına sahiptir. Hem append-logic'i işlemenin hem de toString'inizin nasıl görünmesini istediğinizin biçimlendirilmesinde iyi bir iş çıkarır.

public void toString() {
     ToStringBuilder tsb =  new ToStringBuilder(this);
     tsb.append("a", a);
     tsb.append("b", b)
     return tsb.toString();
}

Görünen çıktıyı döndürür com.blah.YourClass@abc1321f[a=whatever, b=foo].

Veya zincirleme kullanarak daha yoğun bir biçimde:

public void toString() {
     return new ToStringBuilder(this).append("a", a).append("b", b").toString();
}

Ya da sınıfın her alanını dahil etmek için yansıma kullanmak istiyorsanız:

public String toString() {
    return ToStringBuilder.reflectionToString(this);
}

İsterseniz ToString'in stilini de özelleştirebilirsiniz.


4

ToString yöntemini olabildiğince okunabilir hale getirin!

Kitabımdaki tek istisna , bana önemli kaynaklar tükettiğini kanıtlayabilirseniz :) (Evet, bu profilleme anlamına gelir)

Ayrıca Java 5 derleyicisinin, Java'nın önceki sürümlerinde kullanılan el yazısı "StringBuffer" yaklaşımından daha hızlı kod oluşturduğunu unutmayın. "+" Kullanırsanız bu ve gelecekteki geliştirmeler ücretsiz olarak gelir.


3

Mevcut derleyicilerle StringBuilder kullanmanın hala gerekli olup olmadığı konusunda bazı tartışmalar var gibi görünüyor. Bu yüzden 2 sent deneyimimi vereceğimi düşündüm.

Bir var JDBC+ operatörü ile benim makinede 5 hakkında dakika sürer kullanma 10k kayıtları (evet, bir toplu hepsini gerekir.) Sonuç kümesini Java 1.8. Kullanmak stringBuilder.append("")aynı sorgu için bir saniyeden az sürer.

Yani fark çok büyük. Bir döngü StringBuilderiçinde çok daha hızlı.


2
Tartışmaların döngülerin dışında kullanılmasıyla ilgili olduğunu düşünüyorum. Bir döngü içinde kullanmanız gereken bir fikir birliği olduğunu düşünüyorum.
Ürdün

2

Performance wise '+' kullanarak String birleştirme pahalıdır çünkü Stringlerin java'da değişmez olduğu için String'in tamamen yeni bir kopyasını oluşturmak zorundadır. Bu, birleştirme çok sık olduğunda özellikle rol oynar, örneğin: bir döngü içinde. Böyle bir şey yapmaya çalıştığımda IDEA'mın önerdiği şey:

resim açıklamasını buraya girin

Genel kurallar:

  • Tek bir dize ataması içinde, Dize birleştirmenin kullanılması iyidir.
  • Büyük bir karakter verisi bloğu oluşturmak için döngü yapıyorsanız, StringBuffer'a gidin.
  • Bir String'de + = kullanmak her zaman bir StringBuffer kullanmaktan daha az verimli olacaktır, bu yüzden uyarı zilleri çalmalıdır - ancak bazı durumlarda kazanılan optimizasyon, okunabilirlik sorunlarıyla karşılaştırıldığında ihmal edilebilir olacaktır, bu nedenle sağduyunuzu kullanın.

İşte bu konuyla ilgili güzel bir Jon Skeet blogu .


2
Kesinlikle birden çok iş parçacığından senkronize erişim gerektirmedikçe hiçbir zaman StringBuffer kullanmamalısınız. Aksi takdirde, senkronize edilmemiş ve dolayısıyla daha az ek yükü olan StringBuilder'ı tercih edin.
Erki der Loony

1

Bir koleksiyon üzerinde yinelenecek ve StringBuilder kullanacaksanız, Apache Commons Lang ve StringUtils.join () 'a (farklı tatlarda) göz atmak isteyebileceğinizi söyleyebilir miyim ?

Performansından bağımsız olarak, StringBuilders'ı ve milyonlarca kez gibi görünen döngüler için oluşturmanıza gerek kalmaz .


1

İşte Java8'de kontrol ettiklerim

  • Dize birleştirmesini kullanma
  • StringBuilder'ı kullanma

    long time1 = System.currentTimeMillis();
    usingStringConcatenation(100000);
    System.out.println("usingStringConcatenation " + (System.currentTimeMillis() - time1) + " ms");
    
    time1 = System.currentTimeMillis();
    usingStringBuilder(100000);
    System.out.println("usingStringBuilder " + (System.currentTimeMillis() - time1) + " ms");
    
    
    private static void usingStringBuilder(int n)
    {
        StringBuilder str = new StringBuilder();
        for(int i=0;i<n;i++)
            str.append("myBigString");    
    }
    
    private static void usingStringConcatenation(int n)
    {
        String str = "";
        for(int i=0;i<n;i++)
            str+="myBigString";
    }

Çok sayıda dize için dize birleştirme kullanıyorsanız gerçekten bir kabus.

usingStringConcatenation 29321 ms
usingStringBuilder 2 ms

-1

Sanırım StringBuilder ekleme yaklaşımı ile gitmeliyiz. Neden olmak :

  1. String concatenate her seferinde yeni bir dize nesnesi oluşturur (String değişmez bir nesne olduğu için), böylece 3 nesne oluşturur.

  2. String builder ile yalnızca bir nesne [StringBuilder değiştirilebilir ”olur ve diğer dize buna eklenir.


Bu cevap neden reddedildi? docs.oracle.com/javase/8/docs/api/java/util/stream/… - Değişken Azaltma
user1428716

-4

Bunun gibi basit dizeler için kullanmayı tercih ederim

"string".concat("string").concat("string");

Sırayla, bir dize oluşturmak için tercih edilen yöntem StringBuilder, String # concat (), sonra aşırı yüklenmiş + işleci kullanarak söyleyebilirim. StringBuilder, + dizesini kullanmak gibi büyük dizeler çalışırken önemli bir performans artışıdır (performansta büyük bir azalmadır (String boyutu arttıkça katlanarak büyük azalma). .Concat () yöntemini kullanmayla ilgili tek sorun, NullPointerExceptions öğesini atayabilmesidir.


9
JLS, '+' öğesinin StringBuilder'a dönüştürülmesine izin verdiğinden ve büyük olasılıkla tüm JVM'lerin bunu yapması veya daha verimli bir alternatif kullanması nedeniyle concat () kullanmanın '+' değerinden daha kötü performans göstermesi olasıdır. örneğinizde en az bir tam ara dize oluşturmanız ve atmanız gereken concat.
Lawrence Dol
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.