C#或Java:在StringBuilder前面加上字符串?


102

我知道我们可以使用添加字符串StringBuilder。有没有一种方法可以使字符串前置(即在字符串前面添加字符串),StringBuilder以便我们可以保持所StringBuilder提供的性能优势?


我不明白您的问题
莫里斯·佩里

5
前置。这个词是前置的。我猜是预先添加字符串必须类似于一次添加到字符串的两端。
乔尔·穆勒

Answers:



29

前置一个String通常将需要在插入点之后将所有内容复制回backing数组中的某些内容,因此它不如追加到末尾那么快。

但是您可以在Java中这样做(在C#中是相同的,但是方法称为Insert):

aStringBuilder.insert(0, "newText");

11

如果您需要高性能且需要大量准备,则需要编写自己的版本StringBuilder(或使用其他人的版本)。使用标准StringBuilder(尽管从技术上讲,它的实现方式可能有所不同),插入要求在插入点之后复制数据。插入n条文本可能需要O(n ^ 2)时间。

天真的方法是将偏移量char[]和长度添加到后备缓冲区中。如果没有足够的空间容纳前置项,则将数据上移的次数超过严格必要的范围。这可以将性能降低到O(n log n)(我认为)。一种更完善的方法是使缓冲区循环。这样,阵列两端的备用空间将变得连续。


5

您可以尝试扩展方法:

/// <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!

5

您可以反向构建字符串,然后反转结果。您需要支付O(n)的费用,而不是O(n ^ 2)的最坏情况的费用。


2
仅在附加单个字符时有效。否则,您需要颠倒附加的每个字符串,根据字符串的大小和数量,这些字符串将消耗最多(如果不是全部)节省的开销。
雪橇2012年

4

我没有用过,但是Java的绳索很有趣。项目名称是一种文字游戏,使用Rope代替String进行认真的工作。避开前置操作和其他操作的性能损失。值得一看,如果您打算做很多这样的事情。

绳索是弦的高性能替代品。在“绳索:字符串的替代方法”中详细介绍的数据结构,对于诸如前缀,附加,删除和插入之类的常见字符串修改,提供的性能比String和StringBuffer都渐近更好。像字符串一样,绳索是不可变的,因此非常适合在多线程编程中使用。


4

如果要使用Java的StringBuilder类作为前缀,可以执行以下操作:

StringBuilder str = new StringBuilder();
str.Insert(0, "text");

3

如果我对您的理解正确,那么insert方法看起来就可以满足您的要求。只需在偏移量0处插入字符串即可。


2

尝试使用Insert()

StringBuilder MyStringBuilder = new StringBuilder("World!");
MyStringBuilder.Insert(0,"Hello "); // Hello World!

2

从其他评论来看,没有标准的快速方法可以做到这一点。使用StringBuilder的.Insert(0, "text")速度大约是使用缓慢的String串联(基于> 10000 concats的速度)的1-3倍,因此下面的类可能比之前的速度快数千倍!

我已经包括了其他一些基本的功能,如append()subString()length()等双方追加,并预置约两倍快3倍不等慢于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)
    }
}

1

您可以使用一个简单的类自己为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时,您将需要以与您追加时相反的顺序进行Prepend。


0

这应该工作:

aStringBuilder = "newText" + aStringBuilder; 

在.NET中,此方法可以完美地与type的值一起使用string,但不适用于type的值StringBuilder。@ScubaSteve的答案很好用。
Contango 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.