Döngüden sonra bir dizgi arabelleğini / oluşturucuyu temizleme


122

Bir döngüden sonra Java'da dize arabelleğini nasıl temizlersiniz, böylece sonraki yineleme temiz bir dize arabelleği kullanır?


2
Başka bir soru: SB'yi neden döngünün yalnızca bir yinelemesi için kullanıyorsunuz? Başka bir içsel yineleme var mı? Yalnızca A + B + C + D yapmak istiyorsanız SB, layık değildir (Java derleyicisi dahili olarak bir SB kullanacaktır). Koşullu dizge parçaları eklemek isterseniz yardımcı olur, ancak aksi takdirde ... "+" kullanın.
helios

Yanıtlar:


135

Bir seçenek, silme yöntemini aşağıdaki gibi kullanmaktır:

StringBuffer sb = new StringBuffer();
for (int n = 0; n < 10; n++) {
   sb.append("a");

   // This will clear the buffer
   sb.delete(0, sb.length());
}

Başka bir seçenek (bit temizleyici) setLength (int len) kullanır :

sb.setLength(0);

Daha fazla bilgi için Javadoc'a bakın :


15
Daha az saçma olan bir şey, StringBuffer'ı döngü içinde bildirmek olacaktır.
Mark Elliot

11
Ah, bence sb.setLength (0); onu döngü içinde bildirmekten daha temiz ve daha etkilidir. Çözümünüz, StringBuffer kullanmanın performans avantajına aykırı ...
Jon

6
Sanırım performans avantajı, örneklemeyi kaydetmekten değil, dizge değişkenliğinden geliyor. işte hızlı bir 1e8 iterasyon testi: iç döngü (2.97s): ideone.com/uyyTL14w , dış döngü (2.87s): ideone.com/F9lgsIxh
Mark Elliot

6
Performanstan bahsetmişken: Kodunuza çok iş parçacıklı bir senaryoda erişilmediği sürece, StringBuffer yerine StringBuilder kullanmalısınız - bkz. Javadoc: "Mümkün olduğunda, bu sınıfın StringBuffer'a tercih edilerek kullanılması önerilir, çünkü çoğu uygulama ".
Rahel Lüthy

4
SB'yi dışarıda oluşturmanın tek yararı, onun dahili (potansiyel olarak uzun) karakterini [] kaybetmemektir. İlk yineleyicide yeterince büyüdüyse, ikinci döngünün herhangi bir karakteri [] yeniden boyutlandırmasına gerek kalmaz. Ancak avantaj elde etmek için "açık yöntem" dahili dizinin boyutunu korumalıdır. setLength bunu yapar, ancak aynı zamanda SB'deki tüm kullanılmayan karakterleri \ u0000 olarak ayarlar, bu nedenle iyi bir başlangıç ​​kapasitesine sahip yeni bir SB oluşturmak daha az performans gösterir. Döngü içinde bildirmek daha iyidir.
helios

48

Yeniden kullanmanın en kolay yolu StringBufferyöntemi kullanmaktırsetLength()

public void setLength(int newLength)

Davaya sahip olabilirsin

StringBuffer sb = new StringBuffer("HelloWorld");
// after many iterations and manipulations
sb.setLength(0);
// reuse sb

2
@MMahmoud, Kod örneğinde bunun setLengthyerine okunması gerekir setlength.
OnaBai

SetLength kod kaynağını gördüğümde, nasıl çalışıyor ( gist.github.com/ebuildy/e91ac6af2ff8a6b1821d18abf2f8c9e1 ) buradaki sayı her zaman newLength (0) değerinden daha büyük olacak?
Thomas Decaux

20

İki seçeneğiniz var:

Aşağıdakilerden birini kullanın:

sb.setLength(0);  // It will just discard the previous data, which will be garbage collected later.  

Veya kullan:

sb.delete(0, sb.length());  // A bit slower as it is used to delete sub sequence.  

NOT

Döngü içindeki nesneleri StringBufferveya StringBuildernesneleri bildirmekten kaçının , aksi takdirde her yinelemede yeni nesneler oluşturur. Nesnelerin oluşturulması sistem kaynakları, alan gerektirir ve ayrıca zaman alır. Bu nedenle, uzun vadede, mümkünse bir döngü içinde bildirmekten kaçının.



5

Her yineleme için yeni StringBuffer(veya daha iyisi StringBuilder) oluşturmanızı öneririm . Performans farkı gerçekten önemsizdir, ancak kodunuz daha kısa ve daha basit olacaktır.


2
Böyle bir performans farklılığına dair herhangi bir kanıtınız varsa, lütfen paylaşın. (Java'nın en çok nerede kullanıldığına ve hangi tanım altında "çoğunlukla"
ifadesine göre

1
Tahsis etmenin (Java'da yeni anahtar kelime) aynı nesnenin bazı alanlarını değiştirmekten daha pahalı olduğu iyi bilinmektedir. Yüksek oranda yeniden çoğaltabileceğiniz "çoğunlukla" için, tümü Java kullanan 1.5 Milyardan fazla Android Cihaz var.
Louis CAD

1
Bazı durumlarda, kod okunabilirliğinin performanstan daha önemli olduğuna katılıyorum, ancak bu durumda, sadece bir satır kod! Ve size ayırma ve nesne oluşturmanın yanı sıra çöp toplamanın hiç yapmamaktan daha pahalı olduğunu kanıtlayacak bir kıyaslama çalıştırabilirsiniz. Bir döngüde olduğumuz için, alakalı olmaktan daha fazlasıdır.
Louis CAD

1
Knuth'un görüşüne de abone oldum ama onun bakış açısı her zaman doğru değil. Potansiyel olarak CPU döngüleri ve bellek kazanmak için sadece bir satır eklemek kesinlikle kötü değildir. Fikri çok açık alıyorsun. Bir döngünün genellikle birçok kez tekrarlandığını unutmayın. Bir döngü, dikkatli bir şekilde optimize edilmediği takdirde bir mobil cihazda (ve ayrıca sunucu veya bilgisayarda) değerli megabaytları yiyebilen binlerce kez tekrarlanabilir.
Louis CAD

1
Bence ikimiz de bakış açılarımızı yeterince açık bir şekilde ifade ettik ve daha fazla tartışmanın bu yorumlar bölümü için faydalı olmayacağını düşünüyorum. Cevabımın yanlış, yanıltıcı veya eksik olduğunu düşünüyorsanız, o zaman siz - veya herhangi biri - kesinlikle düzenleyebilir ve / veya olumsuz oy verebilir.
Eli Acherkan

5
public void clear(StringBuilder s) {
    s.setLength(0);
}

Kullanımı:

StringBuilder v = new StringBuilder();
clear(v);

Okunabilirlik için bunun en iyi çözüm olduğunu düşünüyorum.


2

Zaten iyi cevap var. StringBuffer için bir karşılaştırma sonucu ekleyin ve StringBuild performans farkı döngüde yeni örnek kullanın veya döngüde setLength (0) kullanın.

Özet: Büyük bir döngüde

  • StringBuilder, StringBuffer'dan çok daha hızlıdır
  • Döngüde yeni StringBuilder örneği oluşturmanın setLength (0) ile hiçbir farkı yoktur. (setLength (0), yeni örnek oluşturmaya göre çok çok çok küçük bir avantaja sahiptir.)
  • StringBuffer, döngüde yeni örnek oluşturarak StringBuilder'dan daha yavaştır
  • StringBuffer'ın setLength (0), döngüde yeni örnek oluşturmaktan çok daha yavaştır.

Çok basit kıyaslama (sadece kodu manuel olarak değiştirdim ve farklı testler yaptım):

public class StringBuilderSpeed {
public static final char ch[] = new char[]{'a','b','c','d','e','f','g','h','i'};

public static void main(String a[]){
    int loopTime = 99999999;
    long startTime = System.currentTimeMillis();
    StringBuilder sb = new StringBuilder();
    for(int i = 0 ; i < loopTime; i++){
        for(char c : ch){
            sb.append(c);
        }
        sb.setLength(0);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Time cost: " + (endTime - startTime));
}

}

Döngüdeki yeni StringBuilder örneği: Zaman maliyeti: 3693, 3862, 3624, 3742

StringBuilder setLength: Zaman maliyeti: 3465, 3421, 3557, 3408

Döngüde yeni StringBuffer örneği: Zaman maliyeti: 8327, 8324, 8284

StringBuffer setLength Zaman maliyeti: 22878, 23017, 22894

Yine StringBuilder setLength, laboratuarımın StringBuffer setLength :-) için bu kadar uzun süre kullanmak için bir sorunla karşılaşmamasını sağlamak için Zaman maliyeti: 3448


0
StringBuffer sb = new SringBuffer();
// do something wiht it
sb = new StringBuffer();

bence bu kod daha hızlı.


3
Bunun performans sorunları olacaktır. Her yinelemeden sonra yeni bir nesne oluşturur
Aditya Singh

@AdityaSingh Bu tamamen makul bir yaklaşım. Kanıt olmadan performans sorunlarını varsaymayın.
shmosel

Neler olup bittiğini anlamaya dayalı şeyleri varsaymak, zekanın temelidir.
rghome
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.