java: ใช้ StringBuilder เพื่อแทรกที่จุดเริ่มต้น


คำตอบ:


189
StringBuilder sb = new StringBuilder();
for(int i=0;i<100;i++){
    sb.insert(0, Integer.toString(i));
}

คำเตือน: ผิดวัตถุประสงค์StringBuilderแต่ทำตามที่คุณถาม


เทคนิคที่ดีกว่า (แม้ว่าจะยังไม่เหมาะ):

  1. ย้อนกลับแต่ละสตริงที่คุณต้องการแทรก
  2. ต่อท้ายแต่ละสตริงเข้ากับไฟล์StringBuilder.
  3. ย้อนกลับทั้งหมด StringBuilderเมื่อคุณทำเสร็จแล้ว

วิธีนี้จะเปลี่ยนโซลูชันO ( n ²) เป็น O ( n )


2
... เนื่องจากทำให้AbstractStringBuilderย้ายเนื้อหาทั้งหมดผ่านดัชนีการแทรกเพื่อหาที่ว่างสำหรับเนื้อหาที่แทรก อย่างไรก็ตามนี่เป็นรายละเอียดการใช้งานไม่ใช่หลักการอย่างใดอย่างหนึ่ง
entonio

1
@entonio: แน่นอน แต่มันเป็นรายละเอียดที่สำคัญมาก :)
user541686

1
ฉันเห็นว่าฉันไม่ควรใช้ StringBuilder แล้วขอบคุณมาก
user685275

@ user685275: ใช่ถ้าคุณต้องการการแทรกย้อนกลับคุณก็ต้องการสิ่งที่สามารถแทรกที่จุดเริ่มต้นของสตริงได้ ฉันคิดว่าวิธีแก้ปัญหาที่ง่ายที่สุดคือเทคนิคข้างต้น (ย้อนกลับสองครั้ง) แม้ว่าคุณอาจจะสร้างคลาสของคุณเองให้ดีขึ้นด้วยอาร์เรย์ถ่าน (อาจต้องการดูเป็น "deque"
user541686

1
@rogerdpack: ฉันจะบอกว่านั่นเป็นกลไกไม่ใช่จุดประสงค์ มีจุดประสงค์เพื่อให้เร็วกว่าการปรับแต่งสตริงโดยไม่มีอาการซึ่งจะไม่เป็นเช่นนั้นหากคุณใช้ผิด
user541686

32

คุณสามารถใช้ได้ strbuilder.insert(0,i);


2
ทำไมถึงได้ไลค์เยอะขนาดนี้! คลาสไม่ได้กำหนดอย่างถูกต้อง - มีเพียงลายเซ็นของการเรียกใช้เมธอดเท่านั้น!
JGFMK

13

บางทีฉันอาจจะขาดอะไรไป แต่คุณต้องการต่อด้วย String ที่มีลักษณะเช่นนี้"999897969594...543210"ใช่ไหม

StringBuilder sb = new StringBuilder();
for(int i=99;i>=0;i--){
    sb.append(String.valueOf(i));
}

แปลกโพสต์นี้ไม่ได้รับการโหวตมากนักในขณะที่ให้การแก้ปัญหาด้วยการจัดการลูปอย่างชาญฉลาด
nom-mon-ir

1
@ nom-mon-ir เขาแค่ย้อนกลับ String มันไม่ตอบโจทย์ว่าจะต่อท้ายด้านซ้ายอย่างไร
Raymond Chenon

บรรลุผลตามที่ต้องการ
Speck

ฉันคิดว่าอาจมีบางกรณีที่คุณสามารถใช้แนวทางนี้แทนการพยายามแฮ็กวิธีทำส่วนแทรกที่จุดเริ่มต้นซึ่งใช้ศักยภาพที่แท้จริงของ StringBuilder อย่างไรก็ตามมีบางกรณีที่คุณไม่สามารถย้อนกลับลูปได้ดังนั้นคุณจึงต้องการคำตอบอื่น ๆ
PhoneixS

7

ในฐานะที่เป็นทางเลือกอื่นคุณสามารถใช้โครงสร้าง LIFO (เช่นสแต็ก) เพื่อจัดเก็บสตริงทั้งหมดและเมื่อคุณทำเสร็จแล้วให้นำออกทั้งหมดและใส่ลงใน StringBuilder โดยธรรมชาติมันจะกลับลำดับของรายการ (สตริง) ที่วางไว้ในนั้น

Stack<String> textStack = new Stack<String>();
// push the strings to the stack
while(!isReadingTextDone()) {
    String text = readText();
    textStack.push(text);
}
// pop the strings and add to the text builder
String builder = new StringBuilder(); 
while (!textStack.empty()) {
      builder.append(textStack.pop());
}
// get the final string
String finalText =  builder.toString();

4
ArrayDequeควรใช้แทนStack. "ชุดการดำเนินการสแต็ก LIFO ที่สมบูรณ์และสม่ำเสมอยิ่งขึ้นมีให้โดยอินเทอร์เฟซ {@link Deque} และการใช้งานซึ่งควรใช้ตามความต้องการของคลาสนี้"
Luna

5

เธรดนี้ค่อนข้างเก่า แต่คุณสามารถนึกถึงโซลูชันแบบวนซ้ำโดยใช้ StringBuilder สิ่งนี้ช่วยป้องกันการประมวลผลย้อนกลับ ฯลฯ เพียงแค่ต้องออกแบบการวนซ้ำของคุณด้วยการเรียกซ้ำและตัดสินใจอย่างรอบคอบสำหรับเงื่อนไขการออก

public class Test {

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        doRecursive(sb, 100, 0);
        System.out.println(sb.toString());
    }

    public static void doRecursive(StringBuilder sb, int limit, int index) {
        if (index < limit) {
            doRecursive(sb, limit, index + 1);
            sb.append(Integer.toString(index));
        }
    }
}

3

ฉันมีข้อกำหนดที่คล้ายกันเมื่อฉันสะดุดกับโพสต์นี้ ฉันต้องการวิธีที่รวดเร็วในการสร้าง String ที่สามารถเติบโตได้จากทั้งสองด้านเช่น เพิ่มตัวอักษรใหม่ที่ด้านหน้าและด้านหลังโดยพลการ ฉันรู้ว่านี่เป็นโพสต์เก่า แต่มันเป็นแรงบันดาลใจให้ฉันลองใช้สองสามวิธีในการสร้างสตริงและฉันคิดว่าฉันจะแบ่งปันสิ่งที่ฉันค้นพบ ฉันยังใช้โครงสร้าง Java 8 บางส่วนในสิ่งนี้ซึ่งสามารถปรับความเร็วให้เหมาะสมในกรณีที่ 4 และ 5

https://gist.github.com/SidWagz/e41e836dec65ff24f78afdf8669e6420

ส่วนสำคัญด้านบนมีรหัสโดยละเอียดที่ทุกคนสามารถเรียกใช้ ฉันใช้วิธีการเพิ่มจำนวนไม่กี่วิธีในเรื่องนี้ 1) ต่อท้าย StringBuilder, 2) แทรกที่ด้านหน้าของ StringBuilder ดังที่แสดงโดย @Mehrdad, 3) แทรกบางส่วนจากด้านหน้าและด้านท้ายของ StringBuilder, 4) การใช้รายการเพื่อต่อท้ายจากส่วนท้าย, 5) การใช้ Deque เพื่อ ต่อท้ายจากด้านหน้า

// Case 2    
StringBuilder build3 = new StringBuilder();
IntStream.range(0, MAX_STR)
                    .sequential()
                    .forEach(i -> {
                        if (i%2 == 0) build3.append(Integer.toString(i)); else build3.insert(0, Integer.toString(i));
                    });
String build3Out = build3.toString();


//Case 5
Deque<String> deque = new ArrayDeque<>();
IntStream.range(0, MAX_STR)
                .sequential()
                .forEach(i -> {
                    if (i%2 == 0) deque.addLast(Integer.toString(i)); else deque.addFirst(Integer.toString(i));
                });

String dequeOut = deque.stream().collect(Collectors.joining(""));

ฉันจะเน้นที่ด้านหน้าต่อท้ายกรณีเท่านั้นเช่น กรณีที่ 2 และกรณีที่ 5 การใช้งาน StringBuilder เป็นการตัดสินใจภายในว่าบัฟเฟอร์ภายในเติบโตอย่างไรซึ่งนอกเหนือจากการย้ายบัฟเฟอร์ทั้งหมดจากซ้ายไปขวาในกรณีที่การต่อท้ายด้านหน้า จำกัด ความเร็ว ในขณะที่เวลาที่ใช้ในการแทรกโดยตรงที่ด้านหน้าของ StringBuilder จะเพิ่มขึ้นเป็นค่าที่สูงมากดังที่แสดงโดย @Mehrdad หากต้องการเพียงแค่มีสตริงที่มีความยาวน้อยกว่า 90k ตัวอักษร (ซึ่งยังคงเป็นจำนวนมาก) ส่วนแทรกด้านหน้าจะ สร้าง String ในเวลาเดียวกับที่ใช้ในการสร้าง String ที่มีความยาวเท่ากันโดยต่อท้าย สิ่งที่ฉันพูดก็คือการลงโทษต่อเวลานั้นแน่นอนและมีขนาดใหญ่มาก แต่เมื่อคุณต้องสร้างสตริงที่ยิ่งใหญ่จริงๆ หนึ่งสามารถใช้ deque และเข้าร่วมสตริงในตอนท้ายดังที่แสดงในตัวอย่างของฉัน

อันที่จริงประสิทธิภาพของกรณีที่ 2 นั้นเร็วกว่ากรณีที่ 1 มากซึ่งฉันดูเหมือนจะไม่เข้าใจ ฉันถือว่าการเติบโตของบัฟเฟอร์ภายในใน StringBuilder จะเหมือนกันในกรณีของการผนวกด้านหน้าและการผนวกด้านหลัง ฉันยังตั้งค่าฮีปขั้นต่ำเป็นจำนวนมากเพื่อหลีกเลี่ยงความล่าช้าในการเติบโตของฮีปหากนั่นจะมีบทบาท บางทีคนที่มีความเข้าใจดีกว่าสามารถแสดงความคิดเห็นด้านล่าง


1
Difference Between String, StringBuilder And StringBuffer Classes
String
String is immutable ( once created can not be changed )object. The object created as a
String is stored in the Constant String Pool.
Every immutable object in Java is thread-safe, which implies String is also thread-safe. String
can not be used by two threads simultaneously.
String once assigned can not be changed.
StringBuffer
StringBuffer is mutable means one can change the value of the object. The object created
through StringBuffer is stored in the heap. StringBuffer has the same methods as the
StringBuilder , but each method in StringBuffer is synchronized that is StringBuffer is thread
safe .
Due to this, it does not allow two threads to simultaneously access the same method. Each
method can be accessed by one thread at a time.
But being thread-safe has disadvantages too as the performance of the StringBuffer hits due
to thread-safe property. Thus StringBuilder is faster than the StringBuffer when calling the
same methods of each class.
String Buffer can be converted to the string by using
toString() method.

    StringBuffer demo1 = new StringBuffer("Hello") ;

// The above object stored in heap and its value can be changed.
/
// Above statement is right as it modifies the value which is allowed in the StringBuffer
StringBuilder
StringBuilder is the same as the StringBuffer, that is it stores the object in heap and it can also
be modified. The main difference between the StringBuffer and StringBuilder is
that StringBuilder is also not thread-safe.
StringBuilder is fast as it is not thread-safe.
/
// The above object is stored in the heap and its value can be modified
/
// Above statement is right as it modifies the value which is allowed in the StringBuilder

1
ยินดีต้อนรับสู่ stackoverflow โปรดใส่คำอธิบายว่าโค้ดใช้ทำอะไรและแก้ปัญหาในคำถามได้อย่างไร
bad_coder

1

คุณสามารถใช้วิธีแทรกกับออฟเซ็ต เมื่อตั้งค่าชดเชยเป็น '0' หมายความว่าคุณกำลังต่อท้าย StringBuilder ของคุณ

StringBuilder sb = new StringBuilder();
for(int i=0;i<100;i++){
    sb.insert(0,i);
}

หมายเหตุ : เนื่องจากวิธีการแทรกสามารถใช้งานได้ทุกประเภทคุณสามารถใช้สำหรับ int, long, char [] เป็นต้น


0

เกี่ยวกับ:

StringBuilder builder = new StringBuilder();
for(int i=99;i>=0;i--){
    builder.append(Integer.toString(i));
}
builder.toString();

หรือ

StringBuilder builder = new StringBuilder();
for(int i=0;i<100;i++){
  builder.insert(0, Integer.toString(i));
}
builder.toString();

แต่ด้วยสิ่งนี้คุณกำลังทำการดำเนินการ O (N ^ 2) แทน O (N)

Snippet จาก java docs:

แทรกการแสดงสตริงของอาร์กิวเมนต์ Object ในลำดับอักขระนี้ เอฟเฟกต์โดยรวมจะเหมือนกับว่าอาร์กิวเมนต์ที่สองถูกแปลงเป็นสตริงโดยวิธีการ String.valueOf(Object)และจากนั้นอักขระของสตริงนั้นจะถูกแทรกลงในลำดับอักขระนี้ที่ออฟเซ็ตที่ระบุ

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