java: usa StringBuilder per inserire all'inizio


94

Potrei farlo solo con String, ad esempio:

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

C'è un modo per ottenere questo risultato con StringBuilder? Grazie.

Risposte:


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

Avvertenza: annulla lo scopo diStringBuilder , ma fa quello che hai chiesto.


Tecnica migliore (anche se ancora non ideale):

  1. Inverti ogni stringa che desideri inserire.
  2. Aggiungi ogni stringa a un file StringBuilder.
  3. Invertire l' intero StringBuilder quando hai finito.

Questo trasformerà una soluzione O ( n ²) in O ( n ).


2
... poiché fa AbstractStringBuilderspostare tutti i contenuti oltre l'indice di inserimento per trovare spazio a quelli inseriti. Tuttavia, questo è un dettaglio di implementazione, non di principio.
entonio

1
@entonio: certo, ma è un dettaglio molto critico . :)
user541686

1
Capisco, sembra che non dovrei usare StringBuilder allora, grazie mille
user685275

@ user685275: Sì, se hai bisogno di un inserimento all'indietro, allora hai davvero bisogno di qualcosa che possa essere inserito all'inizio della stringa. Penso che la soluzione più semplice sia la tecnica sopra (invertire le cose due volte), anche se probabilmente potresti creare una tua classe migliore con gli array di caratteri (potresti voler esaminare "deque" s).
user541686

1
@rogerdpack: direi che questo è il meccanismo, non lo scopo. Lo scopo è essere asintoticamente più veloci della manipolazione delle stringhe, cosa che non sarà se la usi in modo sbagliato.
user541686

32

Puoi usare strbuilder.insert(0,i);


2
Perché ha ottenuto così tanti Mi piace! La classe non è definita correttamente - solo la firma della chiamata al metodo!
JGFMK

13

Forse mi manca qualcosa ma vuoi finire con una stringa che assomiglia a questa "999897969594...543210", giusto?

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

Strano che questo post non abbia ricevuto molte votazioni fornendo la soluzione mediante una manipolazione intelligente del loop,
nom-mon-ir

1
@ nom-mon-ir ha appena invertito la stringa. Non risponde a come aggiungere a sinistra.
Raymond Chenon

Ottiene l'effetto desiderato.
Speck

Penso che potrebbero esserci casi in cui è possibile utilizzare questo approccio invece di cercare di hackerare un modo per eseguire l'inserimento all'inizio che utilizzi il vero potenziale di StringBuilder. Comunque, ci sono casi in cui non puoi invertire il ciclo quindi hai bisogno anche delle altre risposte.
PhoneixS

7

Come soluzione alternativa puoi usare una struttura LIFO (come uno stack) per memorizzare tutte le stringhe e quando hai finito basta prenderle tutte e metterle nello StringBuilder. Naturalmente inverte l'ordine degli elementi (stringhe) inseriti in esso.

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
ArrayDequedovrebbe essere usato al posto di Stack. "Un insieme più completo e coerente di operazioni dello stack LIFO è fornito dall'interfaccia {@link Deque} e dalle sue implementazioni, che dovrebbero essere utilizzate preferibilmente a questa classe."
Luna

5

Questo thread è piuttosto vecchio, ma potresti anche pensare a una soluzione ricorsiva che passa lo StringBuilder da riempire. Ciò consente di prevenire qualsiasi elaborazione inversa, ecc. Devi solo progettare la tua iterazione con una ricorsione e decidere con attenzione una condizione di uscita.

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

Avevo un requisito simile quando sono incappato in questo post. Volevo un modo veloce per costruire una stringa che può crescere da entrambi i lati, ad es. aggiungere arbitrariamente nuove lettere sul davanti e sul retro. So che questo è un vecchio post, ma mi ha ispirato a provare alcuni modi per creare stringhe e ho pensato di condividere le mie scoperte. Sto anche usando alcuni costrutti Java 8 in questo, che avrebbero potuto ottimizzare la velocità nei casi 4 e 5.

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

Il Gist sopra ha il codice dettagliato che chiunque può eseguire. Ho preso pochi modi per far crescere le corde in questo; 1) Aggiungi a StringBuilder, 2) Inserisci davanti a StringBuilder come mostrato da @Mehrdad, 3) Inserisci parzialmente dalla parte anteriore e alla fine di StringBuilder, 4) Uso di un elenco da aggiungere dalla fine, 5) Uso di un Deque to append dalla parte anteriore.

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

Mi concentrerò sull'appendice anteriore solo casi es. caso 2 e caso 5. L'implementazione di StringBuilder decide internamente come cresce il buffer interno, che oltre a spostare tutto il buffer da sinistra a destra in caso di aggiunta frontale limita la velocità. Mentre il tempo impiegato per l'inserimento direttamente nella parte anteriore di StringBuilder cresce fino a valori molto alti, come mostrato da @Mehrdad, se la necessità è di avere solo stringhe di lunghezza inferiore a 90k caratteri (che è ancora molto), l'inserto frontale lo farà costruire una stringa nello stesso tempo necessario per costruire una stringa della stessa lunghezza aggiungendola alla fine. Quello che sto dicendo è che la penalità sul tempo effettivamente calcia ed è enorme, ma solo quando devi costruire stringhe davvero enormi. Si potrebbe usare una deque e unire le stringhe alla fine come mostrato nel mio esempio.

In realtà le prestazioni per il caso 2 sono molto più veloci del caso 1, che non mi sembra di capire. Presumo che la crescita per il buffer interno in StringBuilder sarebbe la stessa in caso di aggiunta anteriore e aggiunta posteriore. Ho persino impostato l'heap minimo su una quantità molto grande per evitare ritardi nella crescita dell'heap, se questo avesse avuto un ruolo. Forse qualcuno che ha una migliore comprensione può commentare di seguito.


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
Benvenuto in stackoverflow. Si prega di includere una spiegazione di ciò che fa il codice e di come risolve il problema nella domanda.
bad_coder

1

È possibile utilizzare il metodo di inserimento con l'offset. come offset impostato a '0' significa che stai aggiungendo alla parte anteriore del tuo StringBuilder.

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

NOTA : poiché il metodo di inserimento accetta tutti i tipi di primitive, è possibile utilizzare per int, long, char [] ecc.


0

Che ne dite di:

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

O

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

Ma con questo, stai facendo l'operazione O (N ^ 2) invece di O (N).

Snippet da documenti java:

Inserisce la rappresentazione di stringa dell'argomento Object in questa sequenza di caratteri. L'effetto complessivo è esattamente come se il secondo argomento fosse convertito in una stringa dal metodo String.valueOf(Object), e i caratteri di quella stringa fossero quindi inseriti in questa sequenza di caratteri all'offset indicato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.