C # hoặc Java: Thêm chuỗi với StringBuilder?


102

Tôi biết chúng ta có thể nối các chuỗi bằng cách sử dụng StringBuilder. Có cách nào chúng ta có thể thêm chuỗi (tức là thêm chuỗi vào trước chuỗi) bằng cách sử dụng StringBuilderđể chúng tôi có thể giữ được các lợi ích về hiệu suất mang StringBuilderlại không?


Tôi không hiểu câu hỏi của bạn
Maurice Perry

5
Thêm trước. Từ được thêm vào trước. Tôi đoán trước một chuỗi phải là một cái gì đó giống như thêm vào cả hai đầu của một chuỗi?
Joel Mueller

Câu trả lời:



29

Việc thêm một Chuỗi thường sẽ yêu cầu sao chép mọi thứ sau khi điểm chèn trở lại một số trong mảng sao lưu, vì vậy sẽ không nhanh bằng việc thêm vào cuối.

Nhưng bạn có thể làm điều đó như thế này trong Java (trong C # thì giống nhau, nhưng phương thức được gọi Insert):

aStringBuilder.insert(0, "newText");

11

Nếu bạn yêu cầu hiệu suất cao với nhiều phần trước, bạn sẽ cần phải viết phiên bản của riêng mình StringBuilder(hoặc sử dụng của người khác). Với tiêu chuẩnStringBuilder (mặc dù về mặt kỹ thuật, nó có thể được thực hiện theo cách khác) chèn yêu cầu sao chép dữ liệu sau điểm chèn. Chèn n đoạn văn bản có thể mất O (n ^ 2) thời gian.

Một cách tiếp cận ngây thơ sẽ là thêm một phần bù vào char[]bộ đệm ủng hộ cũng như độ dài. Khi không có đủ chỗ để chi tiêu trước, hãy di chuyển dữ liệu lên nhiều hơn mức cần thiết. Điều này có thể đưa hiệu suất trở lại O (n log n) (tôi nghĩ vậy). Một cách tiếp cận tinh tế hơn là làm cho bộ đệm theo chu kỳ. Bằng cách đó, không gian trống ở cả hai đầu của mảng trở nên liền nhau.


5

Bạn có thể thử một phương pháp mở rộng:

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

Bạn có thể xây dựng chuỗi ngược lại và sau đó đảo ngược kết quả. Bạn phải chịu chi phí O (n) thay vì chi phí trong trường hợp xấu nhất là O (n ^ 2).


2
Điều đó chỉ hoạt động nếu bạn đang nối các ký tự riêng lẻ. Nếu không, bạn cần phải đảo ngược từng chuỗi mà bạn đã thêm vào sẽ ăn hết nếu không phải là tất cả số tiền tiết kiệm được tùy thuộc vào kích thước và số lượng chuỗi.
Xe trượt tuyết

4

Tôi chưa sử dụng nó nhưng Ropes For Java Nghe có vẻ hấp dẫn. Tên dự án là một cách chơi chữ, sử dụng Rope thay vì String cho những công việc nghiêm túc. Nhận được xung quanh hình phạt hiệu suất cho chi tiêu trước và các hoạt động khác. Đáng xem, nếu bạn đang làm rất nhiều điều này.

Một sợi dây là sự thay thế hiệu suất cao cho Dây. Cơ cấu dữ liệu, được mô tả chi tiết trong "Ropes: a Alternative to Strings", cung cấp hiệu suất tiệm cận tốt hơn so với cả String và StringBuffer cho các sửa đổi chuỗi phổ biến như thêm trước, nối thêm, xóa và chèn. Giống như Chuỗi, dây là bất biến và do đó rất thích hợp để sử dụng trong lập trình đa luồng.


4

Đây là những gì bạn có thể làm Nếu bạn muốn thêm trước bằng cách sử dụng lớp StringBuilder của Java:

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


2

Hãy thử sử dụng Insert ()

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

2

Đánh giá từ các nhận xét khác, không có cách nào nhanh chóng tiêu chuẩn để làm điều này. Sử dụng StringBuilder's.Insert(0, "text") chỉ nhanh hơn khoảng 1-3 lần so với việc sử dụng nối String chậm một cách đáng kinh ngạc (dựa trên> 10000 cặp nối), vì vậy dưới đây là một lớp để thêm trước có khả năng nhanh hơn hàng nghìn lần!

Tôi đã bao gồm một số chức năng cơ bản khác như append(), subString()length() vv Cả hai gắn thêm và prepends khác nhau từ khoảng nhanh gấp hai lần để 3x chậm hơn so với StringBuilder gắn thêm. Giống như StringBuilder, bộ đệm trong lớp này sẽ tự động tăng lên khi văn bản tràn kích thước bộ đệm cũ.

Mã đã được thử nghiệm khá nhiều, nhưng tôi không thể đảm bảo rằng nó không có lỗi.

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

Bạn có thể tự tạo một phần mở rộng cho StringBuilder với một lớp đơn giản:

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
    }
}

Sau đó, chỉ cần thêm:

using Application.Code.Helpers;

Ở đầu bất kỳ lớp nào bạn muốn sử dụng StringBuilder và bất kỳ lúc nào bạn sử dụng intelli-sense với biến StringBuilder, các phương thức Prepend và PrependLine sẽ hiển thị. Chỉ cần nhớ rằng khi bạn sử dụng Thêm trước, bạn sẽ cần Thêm trước theo thứ tự ngược lại so với khi bạn Thêm.


0

Điều này sẽ hoạt động:

aStringBuilder = "newText" + aStringBuilder; 

Trong .NET, điều này hoạt động hoàn hảo với các giá trị của kiểu string, nhưng không hoạt động với các giá trị của kiểu StringBuilder. Câu trả lời từ @ScubaSteve hoạt động tốt.
Contango
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.