Java: Verwenden Sie StringBuilder, um am Anfang einzufügen

89

Ich könnte das nur mit String machen, zum Beispiel:

String str="";
for(int i=0;i<100;i++){
    str=i+str;
}

Gibt es eine Möglichkeit, dies mit StringBuilder zu erreichen? Vielen Dank.

user685275
quelle

Antworten:

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

Warnung: Es macht den Zweck von zunichteStringBuilder , aber es tut, was Sie gefragt haben.


Bessere Technik (obwohl immer noch nicht ideal):

  1. Jeweils umkehren Zeichenfolge um, die Sie einfügen möchten.
  2. Hängen Sie jede Zeichenfolge an a anStringBuilder .
  3. Kehren Sie das Ganze um, StringBuilder wenn Sie fertig sind.

Dies wird wiederum eine O ( n ²) Lösung in O ( n ).

user541686
quelle
2
... da dadurch der AbstractStringBuildergesamte Inhalt über den Einfügeindex hinaus verschoben wird, um Platz für die eingefügten zu finden. Dies ist jedoch ein Implementierungsdetail, kein Prinzip.
Entonio
1
@entonio: In der Tat, aber es ist ein sehr kritisches Detail. :)
user541686
1
Ich sehe, es sieht so aus, als ob ich StringBuilder dann nicht verwenden sollte. Vielen Dank
user685275
@ user685275: Ja, wenn Sie eine Rückwärtseinfügung benötigen, brauchen Sie wirklich etwas, das am Anfang der Zeichenfolge eingefügt werden kann. Ich denke, die einfachste Lösung ist die oben beschriebene Technik (zweimaliges Umkehren), obwohl Sie wahrscheinlich mit char-Arrays eine bessere Klasse für sich bilden könnten (vielleicht möchten Sie sich mit "deque" befassen).
user541686
1
@rogerdpack: Ich würde sagen, das ist der Mechanismus, nicht der Zweck. Der Zweck ist, asymptotisch schneller zu sein als die Manipulation von Strings, was nicht der Fall ist, wenn Sie es falsch verwenden.
user541686
30

Sie können verwenden strbuilder.insert(0,i);

Ratschenfreak
quelle
2
Warum hat das so viele Likes bekommen? Die Klasse ist nicht richtig definiert - nur die Signatur des Methodenaufrufs!
JGFMK
13

Vielleicht fehlt mir etwas, aber Sie möchten mit einem String enden, der so aussieht "999897969594...543210", richtig?

StringBuilder sb = new StringBuilder();
for(int i=99;i>=0;i--){
    sb.append(String.valueOf(i));
}
Speck
quelle
Seltsamerweise wurde dieser Beitrag nicht viel abgestimmt, während die Lösung durch geschickte Manipulation der Schleife bereitgestellt wurde
nom-mon-ir
1
@ nom-mon-ir er kehrt gerade den String um. Es wird nicht beantwortet, wie links angehängt werden soll.
Raymond Chenon
Erreicht den gewünschten Effekt.
Speck
Ich denke, dass es Fälle geben könnte, in denen Sie diesen Ansatz verwenden können, anstatt zu versuchen, am Anfang eine Methode zum Einfügen zu hacken, die das wahre Potenzial von StringBuilder nutzt. Wie auch immer, es gibt Fälle, in denen Sie die Schleife nicht umkehren können, sodass Sie auch die anderen Antworten benötigen.
PhoneixS
7

Als alternative Lösung können Sie eine LIFO-Struktur (wie einen Stapel) verwenden, um alle Zeichenfolgen zu speichern. Wenn Sie fertig sind, nehmen Sie sie einfach heraus und fügen Sie sie in den StringBuilder ein. Es kehrt natürlich die Reihenfolge der darin platzierten Elemente (Zeichenfolgen) um.

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();
Vasile Jureschi
quelle
4
ArrayDequesollte anstelle von verwendet werden Stack. "Ein vollständigerer und konsistenterer Satz von LIFO-Stapeloperationen wird durch die {@link Deque} -Schnittstelle und ihre Implementierungen bereitgestellt, die dieser Klasse vorgezogen werden sollten."
Luna
5

Dieser Thread ist ziemlich alt, aber Sie könnten auch über eine rekursive Lösung nachdenken, die den StringBuilder zum Füllen übergibt. Dies ermöglicht es, eine umgekehrte Verarbeitung usw. zu verhindern. Sie müssen lediglich Ihre Iteration mit einer Rekursion entwerfen und sorgfältig für eine Beendigungsbedingung entscheiden.

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));
        }
    }
}
Benjamin
quelle
3

Ich hatte eine ähnliche Anforderung, als ich über diesen Beitrag stolperte. Ich wollte einen schnellen Weg, um einen String zu bauen, der von beiden Seiten wachsen kann, dh. Fügen Sie beliebig neue Buchstaben auf der Vorder- und Rückseite hinzu. Ich weiß, dass dies ein alter Beitrag ist, aber er hat mich dazu inspiriert, einige Möglichkeiten zum Erstellen von Zeichenfolgen auszuprobieren, und ich dachte, ich würde meine Erkenntnisse teilen. Ich verwende hier auch einige Java 8-Konstrukte, die in den Fällen 4 und 5 die Geschwindigkeit hätten optimieren können.

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

Das obige Gist enthält den detaillierten Code, den jeder ausführen kann. Ich habe nur wenige Möglichkeiten gewählt, um die Saiten zu vergrößern. 1) An StringBuilder anhängen, 2) An die Vorderseite von StringBuilder einfügen, wie von @Mehrdad gezeigt, 3) Teilweise von vorne sowie am Ende des StringBuilder einfügen, 4) Verwenden einer Liste zum Anhängen von Ende an, 5) Verwenden einer Deque an von vorne anhängen.

// 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(""));

Ich werde mich auf die Vorderseite konzentrieren und nur Fälle anhängen, dh. Fall 2 und Fall 5. Die Implementierung von StringBuilder entscheidet intern darüber, wie der interne Puffer wächst, was abgesehen davon, dass der gesamte Puffer im Falle eines Frontanhangs von links nach rechts verschoben wird, die Geschwindigkeit begrenzt. Während die Zeit, die beim direkten Einfügen in die Vorderseite des StringBuilder benötigt wird, auf sehr hohe Werte ansteigt, wie von @Mehrdad gezeigt, wird die vordere Einfügung verwendet, wenn nur Zeichenfolgen mit einer Länge von weniger als 90.000 Zeichen benötigt werden (was immer noch viel ist) Erstellen Sie einen String in der gleichen Zeit, die zum Erstellen eines Strings mit derselben Länge erforderlich ist, indem Sie ihn am Ende anhängen. Was ich damit sagen will ist, dass die Zeitstrafe in der Tat tritt und riesig ist, aber nur, wenn man wirklich große Saiten bauen muss. Man könnte eine Deque verwenden und die Strings am Ende verbinden, wie in meinem Beispiel gezeigt.

Tatsächlich ist die Leistung für Fall 2 viel schneller als für Fall 1, was ich nicht zu verstehen scheine. Ich gehe davon aus, dass das Wachstum für den internen Puffer in StringBuilder im Fall von Front Append und Back Append gleich ist. Ich habe sogar den minimalen Heap auf einen sehr großen Wert festgelegt, um Verzögerungen beim Heap-Wachstum zu vermeiden, wenn dies eine Rolle gespielt hätte. Vielleicht kann jemand, der ein besseres Verständnis hat, unten einen Kommentar abgeben.

Siddharth Wagle
quelle
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
suresh babu garine
quelle
1
Willkommen beim Stackoverflow. Bitte geben Sie in der Frage an, was der Code tut und wie er das Problem löst.
bad_coder
1

Sie können die Einfügemethode mit dem Versatz verwenden. Wenn der Versatz auf '0' gesetzt ist, werden Sie an die Vorderseite Ihres StringBuilder angehängt.

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

HINWEIS : Da die Einfügemethode alle Arten von Grundelementen akzeptiert, können Sie sie für int, long, char [] usw. verwenden.

sachyy
quelle
0

Wie wäre es mit:

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

ODER

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

Aber damit machen Sie die Operation O (N ^ 2) anstelle von O (N).

Ausschnitt aus Java-Dokumenten:

Fügt die Zeichenfolgendarstellung des Object-Arguments in diese Zeichenfolge ein. Der Gesamteffekt ist genau so, als ob das zweite Argument von der Methode in eine Zeichenfolge konvertiert String.valueOf(Object)wurde und die Zeichen dieser Zeichenfolge dann mit dem angegebenen Versatz in diese Zeichenfolge eingefügt wurden.

Ich versuche es
quelle