Rimuovere l'ultimo carattere di StringBuilder?


423

Quando devi scorrere una raccolta e creare una stringa di ciascun dato separata da un delimitatore, alla fine si ottiene sempre un delimitatore aggiuntivo, ad es.

for (String serverId : serverIds) {
  sb.append(serverId);
   sb.append(",");
}

Fornisce qualcosa di simile: serverId_1, serverId_2, serverId_3,

Vorrei cancellare l'ultimo carattere in StringBuilder (senza convertirlo perché ne ho ancora bisogno dopo questo ciclo).


11
Se unendo le stringhe intendi "concatenazione delle stringhe", dipende dal numero di stringhe e dalle loro lunghezze. L'uso di un generatore di stringhe è più efficiente se stai per martellare molte stringhe indipendentemente dalle loro dimensioni poiché le stringhe sono immutabili. Ogni volta che si concatenano stringhe insieme, si crea una nuova stringa risultante (che è in realtà un array di caratteri). I costruttori di stringhe sono essenzialmente un elenco di caratteri che non diventano una stringa immutabile finché non si chiama il metodo toString ().
dyslexicanaboko,

4
Se stai usando Java 8, basta usare StringJoiner: stackoverflow.com/a/29169233/901641
ArtOfWarfare

Risposte:


640

Altri hanno sottolineato il deleteCharAtmetodo, ma ecco un altro approccio alternativo:

String prefix = "";
for (String serverId : serverIds) {
  sb.append(prefix);
  prefix = ",";
  sb.append(serverId);
}

In alternativa, usa la Joinerclasse di Guava :)

A partire da Java 8, StringJoinerfa parte dello standard JRE.


7
@Coronatus: No, perché "" è l' assenza di caratteri, non di un singolo carattere.
Jon Skeet,

31
non eseguirà il prefisso = ","; ogni ciclo di loop influisce sulle prestazioni?
Harish,

21
@Harish: possibilmente, un pochino, anche se molto poco probabile.
Jon Skeet,

4
@Harish - e forse per niente, se l'ottimizzatore svolge la prima iterazione del ciclo.
Stephen C,

6
Apache Commons ha un'altra alternativa a Guava è Joineranche a loro StringUtils. commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/… , java.lang.String)
GoRoS,

419

Un'altra soluzione semplice è:

sb.setLength(sb.length() - 1);

Una soluzione più complicata:

La soluzione di cui sopra presuppone che sb.length() > 0... ovvero sia presente un "ultimo carattere" da rimuovere. Se non riesci a formulare tale presupposto e / o non riesci a gestire l'eccezione che deriverebbe se il presupposto non fosse corretto, controlla prima la lunghezza di StringBuilder; per esempio

// Readable version
if (sb.length() > 0) {
   sb.setLength(sb.length() - 1);
}

o

// Concise but harder-to-read version of the above.
sb.setLength(Math.max(sb.length() - 1, 0));

23
Soluzione molto bella. Minimo impatto sulle prestazioni e minimo codice richiesto :)
Alain O'Dea,

186
if(sb.length() > 0){
    sb.deleteCharAt(sb.length() - 1);
}

33
Questo viene valutato troppo ma non è efficiente, fa un system.arraycopy. Cosa ha detto @Rohit Reddy Korrapolu.
alianos-

13
Non è sicuro sb.length() == 0anche per
Matthias

È sicuro con i personaggi surrogati in gioco?
rogerdpack,

Supponendo che l'ultimo carattere sia il separatore di virgola (come nell'esempio), i surrogati non fanno differenza. Se hai bisogno di generalizzare, sottrai separator.length()invece di 1.
Stephen C

61

A partire da Java 8, la classe String ha un metodo statico join. Il primo argomento è una stringa che vuoi tra ogni coppia di stringhe, e il secondo è un Iterable<CharSequence>(che sono entrambe le interfacce, quindi qualcosa di simile List<String>funziona. Quindi puoi semplicemente fare questo:

String.join(",", serverIds);

Anche in Java 8, è possibile utilizzare la nuova StringJoinerclasse, per scenari in cui si desidera iniziare a costruire la stringa prima di disporre dell'elenco completo degli elementi da inserire in essa.


nvm modificato per te se non ti dispiace, rimosso anche il mio commento
Eugene,

@Eugene - Ho riscritto completamente la risposta su cui concentrarmi String.joinanziché StringJoiner.
ArtOfWarfare,

37

Ottieni la posizione dell'ultima occorrenza del personaggio.

for(String serverId : serverIds) {
 sb.append(serverId);
 sb.append(",");
}
sb.deleteCharAt(sb.lastIndexOf(","));

Poiché lastIndexOfeseguirà una ricerca inversa e sai che lo troverà al primo tentativo, le prestazioni non saranno un problema qui.

MODIFICARE

Dal momento che continuo ad alzarmi sulla mia risposta (grazie gente 😊), vale la pena riguardo a questo:

Su Java 8 in poi sarebbe più leggibile ed esplicito usare StringJoiner . Ha un metodo per un semplice separatore e un sovraccarico per prefisso e suffisso.

Esempi presi da qui: esempio

Esempio usando un semplice separatore:

    StringJoiner mystring = new StringJoiner("-");    

    // Joining multiple strings by using add() method  
    mystring.add("Logan");  
    mystring.add("Magneto");  
    mystring.add("Rogue");  
    mystring.add("Storm");  

    System.out.println(mystring);

Produzione:

Logan-magneto-Rogue-Storm

Esempio con suffisso e prefisso:

    StringJoiner mystring = new StringJoiner(",", "(", ")");    

    // Joining multiple strings by using add() method  
    mystring.add("Negan");  
    mystring.add("Rick");  
    mystring.add("Maggie");  
    mystring.add("Daryl");  

    System.out.println(mystring);

Produzione

(Negan, Rick, Maggie, Daryl)


Sarai sicuro che l'ultimo personaggio sia un ,perché era l'ultima affermazione di for loop. Lo lastInfexOfè di più per la leggibilità e per renderlo un gioco da ragazzi se non vuoi ricordare se è 0-index o no. Inoltre, non è necessario immischiarsi con la lunghezza del costruttore di corde. È solo per comodità.
Reuel Ribeiro,

34

In questo caso,

sb.setLength(sb.length() - 1);

è preferibile in quanto assegna solo l'ultimo valore a '\0'mentre l'eliminazione dell'ultimo carattere lo faSystem.arraycopy


1
La setLengthchiamata non assegna nulla all'ultimo valore. I buffer di stringhe Java non sono chiusi / nulli. In realtà, setLengthsta semplicemente aggiornando un lengthcampo.
Stephen C,

@Rohit Reddy Korrapolu: Ma le arraycopycopie 0 elementi, quindi immagino che possa essere ottimizzato.
maaartinus,

2
Se l'argomento newLength è maggiore o uguale alla lunghezza corrente, vengono aggiunti caratteri null sufficienti ('\ u0000') in modo che la lunghezza diventi l'argomento newLength. Non è così.
Fglez,


11

Un'altra alternativa

for(String serverId : serverIds) {
   sb.append(",");
   sb.append(serverId); 
}
sb.deleteCharAt(0);

2
Dovrebbe essere meglio che rimuovere l'ultimo carattere in quanto ciò richiede calcoli delle dimensioni. A meno che la rimozione del primo carattere non causi lo spostamento dei dati ...
slott

8

In alternativa,

StringBuilder result = new StringBuilder();
for(String string : collection) {
    result.append(string);
    result.append(',');
}
return result.substring(0, result.length() - 1) ;

Utilizzabile in quanto è possibile aggiungere un "." alla fine.
arte il

6
StringBuilder sb = new StringBuilder();
sb.append("abcdef");
sb.deleteCharAt(sb.length() - 1);
assertEquals("abcde",sb.toString());
// true

5

Ancora un'altra alternativa:

public String join(Collection<String> collection, String seperator) {
    if (collection.isEmpty()) return "";

    Iterator<String> iter = collection.iterator();
    StringBuilder sb = new StringBuilder(iter.next());
    while (iter.hasNext()) {
        sb.append(seperator);
        sb.append(iter.next());
    }

    return sb.toString();
}

3

Per evitare il ripristino (influire sulle prestazioni) di prefixutilizzo TextUtils.isEmpty:

            String prefix = "";
            for (String item : list) {
                sb.append(prefix);
                if (TextUtils.isEmpty(prefix))
                    prefix = ",";
                sb.append(item);
            }

A che tipo di pacchetto appartiene TestUtils?
Markus,

@Markus android.text.TextUtils
NickUnuchek,

1

Puoi provare a usare la classe "Joiner" invece di rimuovere l'ultimo carattere dal testo generato;

                List<String> textList = new ArrayList<>();
                textList.add("text1");
                textList.add("text2");
                textList.add("text3");

                Joiner joiner = Joiner.on(",").useForNull("null");
                String output = joiner.join(textList);

               //output : "text1,text2,text3"

1

Sto facendo qualcosa come di seguito:

    StringBuilder stringBuilder = new StringBuilder();
    for (int i = 0; i < value.length; i++) {
        stringBuilder.append(values[i]);
        if (value.length-1) {
            stringBuilder.append(", ");
        }
    }

0

Ecco un'altra soluzione:

for(String serverId : serverIds) {
   sb.append(",");
   sb.append(serverId); 
}

String resultingString = "";
if ( sb.length() > 1 ) {
    resultingString = sb.substring(1);
}

1
Oh, capisco. Stai chiamando sottostringa su StringBuilder non una stringa.
Stephen C

Comunque, questa è solo una piccola variante della soluzione di Zaki del 2010.
Stephen C

0

Personalmente mi piace aggiungere un carattere backspace (o più per "delimitatore" più lungo) alla fine:

for(String serverId : serverIds) {
    sb.append(serverId);
    sb.append(",");
}

sb.append('\b');

Si noti che ha problemi:

  • come \bviene visualizzato dipende dall'ambiente,
  • la length()del Stringcontenuto potrebbe essere diversa dalla lunghezza di caratteri "visibile"

Quando \bsembra OK e la lunghezza non ha importanza, ad esempio la registrazione su una console, questo mi sembra abbastanza buono.


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.