Kullanarak dizeleri ekleyebileceğimizi biliyorum StringBuilder
. Sunulan StringBuilder
performans avantajlarını koruyabilmemiz için dizeleri başa ekleyebileceğimiz (yani bir dizenin önüne dizeler ekleyebileceğimiz) bir yol var mı StringBuilder
?
Kullanarak dizeleri ekleyebileceğimizi biliyorum StringBuilder
. Sunulan StringBuilder
performans avantajlarını koruyabilmemiz için dizeleri başa ekleyebileceğimiz (yani bir dizenin önüne dizeler ekleyebileceğimiz) bir yol var mı StringBuilder
?
Yanıtlar:
Bir Dize'nin başına eklenmesi genellikle ekleme noktasından sonra her şeyin arka dizideki bazılarını geri kopyalamasını gerektirir, bu nedenle sona eklemek kadar hızlı olmayacaktır.
Ancak bunu Java'da şu şekilde yapabilirsiniz (C #'da aynıdır, ancak yöntem denir Insert
):
aStringBuilder.insert(0, "newText");
Çok sayıda ön ekleme ile yüksek performansa ihtiyacınız varsa, kendi sürümünüzü yazmanız StringBuilder
(veya başka birinin sürümünü kullanmanız) gerekir. Standart ile StringBuilder
(teknik olarak farklı şekilde uygulanabilmesine rağmen) ekleme, ekleme noktasından sonra verilerin kopyalanmasını gerektirir. N parça metin eklemek O (n ^ 2) süre alabilir.
Saf bir yaklaşım char[]
, uzunluğun yanı sıra destek tamponuna bir ofset eklemek olacaktır . Bir başa ekleme için yeterli yer olmadığında, verileri kesinlikle gerekenden daha fazla taşıyın. Bu, performansı O (n log n) 'ye düşürür (sanırım). Daha rafine bir yaklaşım, tamponu döngüsel yapmaktır. Bu şekilde dizinin her iki ucundaki boş alan bitişik hale gelir.
Bir uzatma yöntemi deneyebilirsiniz:
/// <summary>
/// kind of a dopey little one-off for StringBuffer, but
/// an example where you can get crazy with extension methods
/// </summary>
public static void Prepend(this StringBuilder sb, string s)
{
sb.Insert(0, s);
}
StringBuilder sb = new StringBuilder("World!");
sb.Prepend("Hello "); // Hello World!
Dizeyi tersten oluşturabilir ve ardından sonucu tersine çevirebilirsiniz. O (n ^ 2) en kötü durum maliyeti yerine O (n) maliyetine maruz kalırsınız.
Kullanmadım ama Java için Halatlar ilgi çekici geliyor. Proje adı kelimeler üzerinde bir oyundur, ciddi işler için String yerine bir Halat kullanın . Ön harcama ve diğer işlemler için performans cezasını ortadan kaldırır. Bunu çok yapacaksanız, bir göz atmaya değer.
Bir halat, Teller için yüksek performanslı bir alternatiftir. "Halatlar: Dizelere Bir Alternatif" bölümünde ayrıntılı olarak açıklanan veri yapısı, başa ekleme, ekleme, silme ve ekleme gibi yaygın dizgi modifikasyonları için hem String hem de StringBuffer'dan asimptotik olarak daha iyi performans sağlar. Dizeler gibi, ipler de değişmezdir ve bu nedenle çok iş parçacıklı programlamada kullanım için çok uygundur.
Sizi doğru anlarsam, ekleme yöntemi istediğinizi yapacak gibi görünür. Dizeyi 0 ofsetine eklemeniz yeterlidir.
Diğer yorumlara bakılırsa, bunu yapmanın standart bir hızlı yolu yoktur. StringBuilder'ın Kullanımı.Insert(0, "text")
, acı verici derecede yavaş String birleştirme kullanmaktan yaklaşık 1-3 kat daha hızlıdır (> 10000 bitiştirmeye dayalı olarak), bu nedenle aşağıda potansiyel olarak binlerce kez daha hızlı bir şekilde başa dönecek bir sınıf bulunmaktadır!
Ben gibi diğer bazı temel işlevleri yer verdik append()
, subString()
ve length()
vb Hem ekler ve prepends yavaş StringBuilder ekler daha 3x yaklaşık iki kat daha hızlı değişir. StringBuilder gibi, bu sınıftaki arabellek, metin eski arabellek boyutunu aştığında otomatik olarak artacaktır.
Kod epeyce test edildi, ancak hatasız olduğunu garanti edemem.
class Prepender
{
private char[] c;
private int growMultiplier;
public int bufferSize; // Make public for bug testing
public int left; // Make public for bug testing
public int right; // Make public for bug testing
public Prepender(int initialBuffer = 1000, int growMultiplier = 10)
{
c = new char[initialBuffer];
//for (int n = 0; n < initialBuffer; n++) cc[n] = '.'; // For debugging purposes (used fixed width font for testing)
left = initialBuffer / 2;
right = initialBuffer / 2;
bufferSize = initialBuffer;
this.growMultiplier = growMultiplier;
}
public void clear()
{
left = bufferSize / 2;
right = bufferSize / 2;
}
public int length()
{
return right - left;
}
private void increaseBuffer()
{
int nudge = -bufferSize / 2;
bufferSize *= growMultiplier;
nudge += bufferSize / 2;
char[] tmp = new char[bufferSize];
for (int n = left; n < right; n++) tmp[n + nudge] = c[n];
left += nudge;
right += nudge;
c = new char[bufferSize];
//for (int n = 0; n < buffer; n++) cc[n]='.'; // For debugging purposes (used fixed width font for testing)
for (int n = left; n < right; n++) c[n] = tmp[n];
}
public void append(string s)
{
// If necessary, increase buffer size by growMultiplier
while (right + s.Length > bufferSize) increaseBuffer();
// Append user input to buffer
int len = s.Length;
for (int n = 0; n < len; n++)
{
c[right] = s[n];
right++;
}
}
public void prepend(string s)
{
// If necessary, increase buffer size by growMultiplier
while (left - s.Length < 0) increaseBuffer();
// Prepend user input to buffer
int len = s.Length - 1;
for (int n = len; n > -1; n--)
{
left--;
c[left] = s[n];
}
}
public void truncate(int start, int finish)
{
if (start < 0) throw new Exception("Truncation error: Start < 0");
if (left + finish > right) throw new Exception("Truncation error: Finish > string length");
if (finish < start) throw new Exception("Truncation error: Finish < start");
//MessageBox.Show(left + " " + right);
right = left + finish;
left = left + start;
}
public string subString(int start, int finish)
{
if (start < 0) throw new Exception("Substring error: Start < 0");
if (left + finish > right) throw new Exception("Substring error: Finish > string length");
if (finish < start) throw new Exception("Substring error: Finish < start");
return toString(start,finish);
}
public override string ToString()
{
return new string(c, left, right - left);
//return new string(cc, 0, buffer); // For debugging purposes (used fixed width font for testing)
}
private string toString(int start, int finish)
{
return new string(c, left+start, finish-start );
//return new string(cc, 0, buffer); // For debugging purposes (used fixed width font for testing)
}
}
StringBuilder için basit bir sınıfla kendiniz bir uzantı oluşturabilirsiniz:
namespace Application.Code.Helpers
{
public static class StringBuilderExtensions
{
#region Methods
public static void Prepend(this StringBuilder sb, string value)
{
sb.Insert(0, value);
}
public static void PrependLine(this StringBuilder sb, string value)
{
sb.Insert(0, value + Environment.NewLine);
}
#endregion
}
}
Ardından şunu ekleyin:
using Application.Code.Helpers;
StringBuilder'ı kullanmak istediğiniz herhangi bir sınıfın tepesinde ve bir StringBuilder değişkeniyle intelli-sense'i her kullandığınızda, Prepend ve PrependLine yöntemleri görünecektir. Başa Eklemeyi kullandığınızda, Eklemekte olduğunuza göre ters sırada Başa Eklemeniz gerekeceğini unutmayın.