我知道我们可以使用StringBuilder
. 有没有一种方法可以预先添加字符串(即在字符串前面添加字符串),StringBuilder
这样我们就可以保持StringBuilder
提供的性能优势?
12 回答
前置字符串通常需要将插入点之后的所有内容复制回支持数组中的某个位置,因此它不会像追加到末尾那样快。
但是你可以在 Java 中这样做(在 C# 中是一样的,但是方法被调用Insert
):
aStringBuilder.insert(0, "newText");
如果您需要具有大量前置功能的高性能,则需要编写自己的版本StringBuilder
(或使用其他人的版本)。使用标准StringBuilder
(尽管从技术上讲,它可以以不同的方式实现)插入需要在插入点之后复制数据。插入 n 段文本可能需要 O(n^2) 时间。
一种天真的方法是在后备char[]
缓冲区中添加偏移量以及长度。当没有足够的空间进行前置时,将数据向上移动超过绝对必要的量。这可以将性能降低到 O(n log n) (我认为)。一种更精细的方法是使缓冲区循环。这样,阵列两端的空闲空间就变得连续。
如果您想使用 Java 的 StringBuilder 类预先设置,您可以执行以下操作:
StringBuilder str = new StringBuilder();
str.Insert(0, "text");
您可以尝试扩展方法:
/// <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!
您可以反向构建字符串,然后反转结果。您会产生 O(n) 成本,而不是 O(n^2) 最坏情况成本。
如果我理解正确,插入方法看起来会做你想做的事。只需在偏移量 0 处插入字符串。
我没有用过它,但Ropes For Java听起来很有趣。项目名称是文字游戏,认真工作时使用绳索而不是字符串。绕过前置操作和其他操作的性能损失。值得一看,如果你要做很多这样的事情。
绳索是弦乐的高性能替代品。在“Ropes: an Alternative to Strings”中详细描述的数据结构,对于常见的字符串修改(如 prepend、append、delete 和 insert)提供了比 String 和 StringBuffer 更好的性能。与字符串一样,绳索是不可变的,因此非常适合用于多线程编程。
尝试使用插入()
StringBuilder MyStringBuilder = new StringBuilder("World!");
MyStringBuilder.Insert(0,"Hello "); // Hello World!
从其他评论来看,没有标准的快速方法可以做到这一点。使用 StringBuilder 的.Insert(0, "text")
速度大约只有使用非常慢的字符串连接(基于 >10000 个连接)的 1-3 倍,所以下面是一个类,它的前置速度可能会快数千倍!
我已经包含了一些其他基本功能,例如append()
,subString()
等等length()
。追加和前置的速度从 StringBuilder 追加的两倍到慢三倍不等。与 StringBuilder 一样,当文本溢出旧的缓冲区大小时,此类中的缓冲区会自动增加。
该代码已经过大量测试,但我不能保证它没有错误。
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 创建一个扩展:
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
}
}
然后,只需添加:
using Application.Code.Helpers;
在您想在其中使用 StringBuilder 的任何类的顶部,以及任何时候将智能感知与 StringBuilder 变量一起使用时,都会显示 Prepend 和 PrependLine 方法。请记住,当您使用 Prepend 时,您需要以与 Appending 相反的顺序进行 Prepend。
这应该有效:
aStringBuilder = "newText" + aStringBuilder;