我们如何使用 StringBuilder 在字符串前面添加字符串?

问题描述 投票:0回答:13

我知道我们可以使用

StringBuilder
附加字符串。有没有一种方法可以使用
StringBuilder
预先添加字符串(即在字符串前面添加字符串),这样我们就可以保持
StringBuilder
提供的性能优势?

c# java stringbuilder
13个回答
255
投票

使用位置参数设置为 0 的 insert 方法与前置相同(即在开头插入)。

C# 示例:

varStringBuilder.Insert(0, "someThing");

Java 示例:

varStringBuilder.insert(0, "someThing");

它适用于 C#Java


38
投票

前置字符串通常需要将插入点之后的所有内容复制回支持数组中的一些内容,因此它不会像附加到末尾那么快。

但是你可以在Java中这样做(在C#中是一样的,但是方法被称为

Insert
):

aStringBuilder.insert(0, "newText");

16
投票

如果您需要具有大量前置的高性能,则需要编写自己的

StringBuilder
版本(或使用其他人的版本)。使用标准
StringBuilder
(尽管技术上可以以不同的方式实现)插入需要在插入点之后复制数据。插入 n 段文本可能需要 O(n^2) 时间。

一个简单的方法是在支持

char[]
缓冲区中添加偏移量以及长度。当没有足够的空间用于前置时,请将数据向上移动超过严格需要的位置。这可以将性能降低到 O(n log n) (我认为)。更精细的方法是使缓冲区循环。这样数组两端的空闲空间就变得连续了。


9
投票

如果您想使用 Java 的 StringBuilder 类进行前置,您可以执行以下操作:

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

8
投票

您可以尝试扩展方法:

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

6
投票

您可以反向构建字符串,然后反转结果。 您会产生 O(n) 成本,而不是 O(n^2) 最坏情况成本。


4
投票

如果我理解正确的话,插入方法看起来会做你想做的事。只需在偏移量 0 处插入字符串即可。


4
投票

我还没有使用过它,但是 Ropes For Java 听起来很有趣。该项目名称是一个文字游戏,对于严肃的工作,请使用绳子而不是绳子。 避免前置操作和其他操作的性能损失。 如果您打算做很多这样的事情,值得一看。

绳索是一种高性能 字符串的替代品。这 数据结构,详细描述于 “绳索:绳索的替代品”, 提供渐进更好的 性能优于 String 和 普通字符串的StringBuffer 修改如前置、附加、 删除,然后插入。就像弦乐一样, 绳索是不可变的,因此 非常适合在多线程中使用 编程。


3
投票

尝试使用 Insert()

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

3
投票

从其他评论来看,没有标准的快速方法可以做到这一点。使用 StringBuilder 的

.Insert(0, "text")
大约只比使用极其缓慢的字符串连接(基于 >10000 次连接)快 1-3 倍,因此下面是一个可以更快地进行数千倍预置的类!

我添加了一些其他基本功能,例如

append()
subString()
length()
等。追加和前置的速度比 StringBuilder 追加的速度大约是两倍到慢 3 倍。与 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)
    }
}

2
投票

您可以使用一个简单的类自己为 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。


0
投票

这应该有效:

aStringBuilder = "newText" + aStringBuilder; 

0
投票

我更喜欢使用 char[] 存储字符串,然后转换为 String/StringBuilder。

char[] 比 String 和 StringBuilder 快得多。

package org.dakshay;
import java.util.Arrays;
public class Benchmark {


public static void main(String[] args) {
    int size = 1000000;
    benchmark_StringBuilder(size);
    benchmark_String(size);
    benchmark_Char_Array(size);
}

private static void benchmark_Char_Array(int size) {
    char[] arr = new char[size];
    Long before = System.currentTimeMillis();
    Arrays.fill(arr, 'h');
    String s = Arrays.toString(arr);
    System.out.println("Time Taken by Char Array in ms: " + (System.currentTimeMillis() - before));
}

private static void benchmark_String(int size) {
    String s = "";
    Long before = System.currentTimeMillis();
    for(int i=0; i<size; i++){
        s="h"+s;
    }
    System.out.println("Time Taken by String in ms: " + (System.currentTimeMillis() - before));

}

private static void benchmark_StringBuilder(int size) {
    StringBuilder sb = new StringBuilder();
    Long before = System.currentTimeMillis();
    for(int i=0; i<size; i++){
        sb.insert(0,"h");
    }
    System.out.println("Time Taken by StringBuilder in ms: " + (System.currentTimeMillis() - before));
}

}

这里我在第 0 个索引处添加前缀,直到 100 万个请求

在 JDK 22.0.2、RAM 16GB SSD 512 i5-12450H 上进行基准测试,得出以下结果 -

Time Taken by StringBuilder in ms: 10204
Time Taken by String in ms: 78268
Time Taken by Char Array in ms: 49

Char 数组在 1M 请求中产生更快的索引填充,用时 49 毫秒,其次是 StringBuilder,用时 10 秒,然后是 String,用时 78 秒。

© www.soinside.com 2019 - 2024. All rights reserved.