Letztes Zeichen eines StringBuilder entfernen?

421

Wenn Sie eine Sammlung durchlaufen und aus jedem durch ein Trennzeichen getrennten Datenzeichen eine Zeichenfolge erstellen müssen, erhalten Sie am Ende immer ein zusätzliches Trennzeichen, z

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

Gibt etwas wie: serverId_1, serverId_2, serverId_3,

Ich möchte das letzte Zeichen im StringBuilder löschen (ohne es zu konvertieren, da ich es nach dieser Schleife noch brauche).

Matthew
quelle
11
Wenn Sie mit dem Verbinden von Zeichenfolgen "Zeichenfolgenverkettung" meinen, hängt dies von der Anzahl der Zeichenfolgen und ihrer Länge ab. Die Verwendung eines String-Builders ist effizienter, wenn Sie unabhängig von ihrer Größe viele Strings einschlagen möchten, da die Strings unveränderlich sind. Jedes Mal, wenn Sie Zeichenfolgen miteinander verketten, erstellen Sie eine neue resultierende Zeichenfolge (die eigentlich ein Zeichenarray ist). String Builder sind im Wesentlichen eine Liste von Zeichen, die erst dann zu einem unveränderlichen String werden, wenn Sie die toString () -Methode aufrufen.
Legasthenikeraboko
4
Wenn Sie Java 8 verwenden, verwenden Sie einfach StringJoiner: stackoverflow.com/a/29169233/901641
ArtOfWarfare

Antworten:

640

Andere haben auf die deleteCharAtMethode hingewiesen , aber hier ist ein anderer alternativer Ansatz:

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

Alternativ können Sie die JoinerKlasse von Guava verwenden :)

Ab Java 8 StringJoinerist Teil der Standard-JRE.

Jon Skeet
quelle
7
@Coronatus: Nein, denn "" ist das Fehlen von Zeichen, kein einzelnes Zeichen.
Jon Skeet
31
Präfix = "," wird nicht ausgeführt; Jeder Schleifenzyklus beeinflusst die Leistung?
Harish
21
@ Harish: Möglicherweise ein winziges Stückchen - sehr unwahrscheinlich, dass es von Bedeutung ist.
Jon Skeet
4
@ Harish - und möglicherweise überhaupt nicht, wenn der Optimierer die erste Schleifeniteration abrollt.
Stephen C
6
Apache Commons hat eine weitere Alternative zu Guava ist Joinerauch in ihr StringUtils. commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/… , java.lang.String)
GoRoS
419

Eine andere einfache Lösung ist:

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

Eine kompliziertere Lösung:

Die obige Lösung geht davon aus sb.length() > 0 ... dh ein "letztes Zeichen" entfernt werden muss. Wenn Sie diese Annahme nicht treffen können und / oder die Ausnahme nicht behandeln können, die sich ergeben würde, wenn die Annahme falsch ist, überprüfen Sie zuerst die Länge des StringBuilder. z.B

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

oder

// Concise but harder-to-read version of the above.
sb.setLength(Math.max(sb.length() - 1, 0));
Stephen C.
quelle
23
Sehr schöne Lösung. Geringste Auswirkung auf die Leistung und geringster Codebedarf :)
Alain O'Dea
186
if(sb.length() > 0){
    sb.deleteCharAt(sb.length() - 1);
}
Prahler
quelle
33
Dies wird zu sehr positiv bewertet, ist aber nicht effizient. Es führt eine system.arraycopy durch. Was @Rohit Reddy Korrapolu gesagt hat.
Alianos
13
Es ist für unsichere sb.length() == 0auch
Matthias
Ist dies sicher, wenn Ersatzpaarcharaktere im Spiel sind?
Rogerdpack
Unter der Annahme, dass das letzte Zeichen das Komma-Trennzeichen ist (wie im Beispiel), machen Ersatzzeichen keinen Unterschied. Wenn Sie verallgemeinern müssen, dann subtrahieren separator.length()statt 1.
Stephen C
61

Ab Java 8 verfügt die String-Klasse über eine statische Methode join. Das erste Argument ist eine Zeichenfolge, die Sie zwischen jedem Zeichenfolgenpaar haben möchten, und das zweite ist eine Zeichenfolge Iterable<CharSequence>(beides Schnittstellen, also List<String>funktioniert so etwas wie etwas . Sie können also einfach Folgendes tun:

String.join(",", serverIds);

In Java 8 können Sie die neue StringJoinerKlasse auch für Szenarien verwenden, in denen Sie mit dem Erstellen der Zeichenfolge beginnen möchten, bevor Sie die vollständige Liste der Elemente zum Einfügen haben.

ArtOfWarfare
quelle
nvm für Sie bearbeitet, wenn Sie nichts dagegen haben, entfernte auch meinen Kommentar
Eugene
@Eugene - Ich habe die Antwort komplett neu geschrieben, um mich darauf zu konzentrieren, String.joinanstatt StringJoiner.
ArtOfWarfare
37

Holen Sie sich einfach die Position des letzten Zeichenvorkommens.

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

Da lastIndexOfeine umgekehrte Suche durchgeführt wird und Sie wissen, dass diese beim ersten Versuch gefunden wird, ist die Leistung hier kein Problem.

BEARBEITEN

Da ich meine Antwort immer wieder auf den neuesten Stand bringe (danke Leute 😊), lohnt es sich, dies zu berücksichtigen:

Auf Java 8 weiter wäre es nur besser lesbar und explizit verwenden StringJoiner . Es gibt eine Methode für ein einfaches Trennzeichen und eine Überladung für Präfix und Suffix.

Beispiele aus hier: Beispiel

Beispiel mit einfachem Trennzeichen:

    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);

Ausgabe:

Logan-Magneto-Rogue-Storm

Beispiel mit Suffix und Präfix:

    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);

Ausgabe

(Negan, Rick, Maggie, Daryl)

Reuel Ribeiro
quelle
Sie werden sicher sein, dass das letzte Zeichen ein ist, ,weil es die letzte Aussage des war for loop. Das lastInfexOfist mehr , um die Lesbarkeit und es ein Kinderspiel zu machen , wenn Sie wollen nicht daran erinnern , ob es 0-indiziert ist oder nicht. Außerdem müssen Sie sich nicht in die Länge des Stringbuilders einmischen. Es ist nur für die Bequemlichkeit.
Reuel Ribeiro
34

In diesem Fall,

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

ist vorzuziehen, da nur der letzte Wert zugewiesen wird, '\0'während das Löschen des letzten Zeichens dies tutSystem.arraycopy

Rohit Reddy Korrapolu
quelle
1
Der setLengthAufruf weist dem letzten Wert nichts zu. Java-String-Puffer werden nicht mit Null / Null abgeschlossen. In der Tat setLengthwird einfach ein lengthFeld aktualisiert .
Stephen C
@Rohit Reddy Korrapolu: Aber das arraycopykopiert 0 Elemente, also denke ich, dass es weg optimiert werden kann.
Maaartinus
2
Wenn das Argument newLength größer oder gleich der aktuellen Länge ist, werden ausreichend Nullzeichen ('\ u0000') angehängt, sodass die Länge zum Argument newLength wird. Welches ist nicht der Fall.
Fglez
11

Eine andere Alternative

for(String serverId : serverIds) {
   sb.append(",");
   sb.append(serverId); 
}
sb.deleteCharAt(0);
Rafiq
quelle
2
Sollte besser sein als das Entfernen des letzten Zeichens, da dies Größenberechnungen erfordert. Es sei denn, das Entfernen des ersten Zeichens führt dazu, dass Daten verschoben werden ...
Slott
8

Alternative,

StringBuilder result = new StringBuilder();
for(String string : collection) {
    result.append(string);
    result.append(',');
}
return result.substring(0, result.length() - 1) ;
Zaki
quelle
Verwendbar, da Sie ein "." Am Ende.
kunstvoll
6
StringBuilder sb = new StringBuilder();
sb.append("abcdef");
sb.deleteCharAt(sb.length() - 1);
assertEquals("abcde",sb.toString());
// true
Antoine
quelle
5

Noch eine Alternative:

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();
}
Jason Day
quelle
3

Um eine erneute Aktivierung (Beeinträchtigung der Leistung) der prefixVerwendung von TextUtils.isEmpty zu vermeiden :

            String prefix = "";
            for (String item : list) {
                sb.append(prefix);
                if (TextUtils.isEmpty(prefix))
                    prefix = ",";
                sb.append(item);
            }
NickUnuchek
quelle
Zu welcher Art von Paket gehört TestUtils?
Markus
@ Markus android.text.TextUtils
NickUnuchek
1

Sie können versuchen, die Joiner-Klasse zu verwenden, anstatt das letzte Zeichen aus Ihrem generierten Text zu entfernen.

                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"
oguzhan
quelle
1

Ich mache so etwas wie unten:

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

Hier ist eine andere Lösung:

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

String resultingString = "";
if ( sb.length() > 1 ) {
    resultingString = sb.substring(1);
}
Stephan
quelle
1
Oh ... ich verstehe. Sie rufen im StringBuilder einen Teilstring auf, keinen String.
Stephen C
Aber trotzdem ist dies nur eine kleine Variante von Zakis Lösung aus dem Jahr 2010.
Stephen C
0

Ich persönlich möchte am Ende ein Backspace-Zeichen (oder mehr für ein längeres "Trennzeichen") anhängen:

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

sb.append('\b');

Beachten Sie, dass es Probleme gibt:

  • Wie \bangezeigt wird, hängt von der Umgebung ab.
  • die length()der StringInhalt kann von der Länge der „visibile“ Zeichen unterschiedlich sein

Wenn es gut \baussieht und die Länge keine Rolle spielt, z. B. das Anmelden an einer Konsole, scheint mir das gut genug zu sein.

Attacktiv
quelle
-1

stringBuilder.Remove (stringBuilder.Length - 1, 1);

Mohamed Farook Mohamed Fazrin
quelle