C # หรือ Java: เพิ่มสตริงด้วย StringBuilder?


104

ฉันรู้ว่าเราสามารถต่อท้ายสตริงโดยใช้StringBuilder. มีวิธีที่เราสามารถเพิ่มสตริงไว้ข้างหน้าสตริงได้หรือไม่โดยใช้StringBuilderเพื่อให้เราสามารถรักษาผลประโยชน์ด้านประสิทธิภาพที่StringBuilderมีให้ได้หรือไม่?


ฉันไม่เข้าใจคำถามของคุณ
Maurice Perry

5
นำหน้า คำนี้อยู่ข้างหน้า การเพิ่มสตริงล่วงหน้าต้องเป็นสิ่งที่ต้องการเพิ่มที่ปลายทั้งสองด้านของสตริงพร้อมกันฉันเดาว่า?
Joel Mueller

คำตอบ:


166

การใช้วิธีการแทรกโดยกำหนดพารามิเตอร์ตำแหน่งเป็น 0 จะเหมือนกับการป้อนล่วงหน้า (เช่นการแทรกที่จุดเริ่มต้น)

ตัวอย่างคือ: varStringBuilder.insert(0, "someThing");

ใช้ได้ทั้งกับC #และJava


7
แทรก StringBuilder สำหรับ java: java.sun.com/j2se/1.5.0/docs/api/java/lang/…
Matthew Farwell

JavaDoc ที่ถูกต้องสำหรับ API ที่เกี่ยวข้องคือdocs.oracle.com/javase/1.5.0/docs/api/java/lang/…
เลื่อน

29

การเตรียมสตริงล่วงหน้ามักจะต้องคัดลอกทุกอย่างหลังจากจุดแทรกกลับบางส่วนในอาร์เรย์สำรองดังนั้นจึงไม่รวดเร็วเท่ากับการต่อท้าย

แต่คุณสามารถทำได้ใน 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
จะใช้ได้เฉพาะเมื่อคุณต่อท้ายอักขระแต่ละตัว มิฉะนั้นคุณจะต้องย้อนกลับแต่ละสตริงที่คุณต่อท้ายซึ่งจะกินมากที่สุดหากประหยัดได้ไม่ทั้งหมดขึ้นอยู่กับขนาดและจำนวนสตริง
เลื่อน

4

ฉันไม่ได้ใช้ แต่Ropes For Java Sounds น่าสนใจ ชื่อโครงการเป็นการเล่นคำใช้RopeแทนStringเพื่อการทำงานที่จริงจัง ได้รับการลงโทษด้านประสิทธิภาพสำหรับการดำเนินการล่วงหน้าและการดำเนินการอื่น ๆ คุ้มค่าถ้าคุณจะทำสิ่งนี้เป็นจำนวนมาก

เชือกเป็นสิ่งทดแทนที่มีประสิทธิภาพสูงสำหรับสตริง โครงสร้างข้อมูลที่อธิบายโดยละเอียดใน "Ropes: an Alternative to Strings" ให้ประสิทธิภาพที่ดีกว่าทั้ง String และ StringBuffer แบบไม่มีอาการสำหรับการปรับเปลี่ยนสตริงทั่วไปเช่นการต่อท้ายผนวกลบและแทรก เช่นเดียวกับสตริงเชือกจะไม่เปลี่ยนรูปจึงเหมาะอย่างยิ่งสำหรับใช้ในการเขียนโปรแกรมแบบมัลติเธรด


4

นี่คือสิ่งที่คุณสามารถทำได้หากคุณต้องการนำหน้าโดยใช้คลาส StringBuilder ของ Java:

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



2

เมื่อพิจารณาจากความคิดเห็นอื่น ๆ ไม่มีวิธีที่รวดเร็วมาตรฐานในการทำเช่นนี้ การใช้ StringBuilder .Insert(0, "text")นั้นเร็วพอ ๆ กับการใช้การต่อสตริงที่ช้าลงอย่างเจ็บปวดโดยประมาณ (อิงจาก> 10,000 concats) ดังนั้นด้านล่างนี้คือคลาสที่จะนำหน้าเร็วกว่าหลายพันเท่า!

เราได้รวมฟังก์ชันการทำงานบางขั้นพื้นฐานอื่น ๆ เช่นappend(), subString()และlength()อื่น ๆ ทั้งผนวกและ prepends แตกต่างจากสองเท่ารวดเร็ว 3x ช้ากว่าผนวก 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 และทุกครั้งที่คุณใช้ intelli-sense กับตัวแปร StringBuilder เมธอด Prepend และ PrependLine จะปรากฏขึ้น เพียงจำไว้ว่าเมื่อคุณใช้ Prepend คุณจะต้องนำหน้าในลำดับย้อนกลับมากกว่าที่คุณกำลังต่อท้าย


0

สิ่งนี้ควรใช้งานได้:

aStringBuilder = "newText" + aStringBuilder; 

ใน .NET นี้ทำงานอย่างสมบูรณ์แบบที่มีค่าชนิดแต่ไม่ได้ทำงานกับค่านิยมของประเภทstring StringBuilderคำตอบจาก @ScubaSteve ทำงานได้ดี
Contango
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.