Java-Entsprechungen von C # String.Format () und String.Join ()

111

Ich weiß, dass dies eine neue Frage ist, aber gibt es Entsprechungen zu C # -Stringoperationen in Java?

Insbesondere spreche ich über String.Formatund String.Join.

Omar Kooheji
quelle
Es gibt also ein String.format, aber ich muss meinen eigenen Join rollen.
Omar Kooheji
1
Für join (), wie ich diese Antwort: stackoverflow.com/a/6116469/562139
scorpiodawg

Antworten:

92

Das Java String-Objekt verfügt über eine formatMethode (ab 1.5), jedoch keine joinMethode.

Um eine Reihe nützlicher String-Dienstprogrammmethoden zu erhalten, die noch nicht enthalten sind, können Sie org.apache.commons.lang.StringUtils verwenden .

Grant Wagner
quelle
13
Google-Sammlungen: google-collections.googlecode.com hat auch einen Joiner.
Ron
10
Zu Ihrer Information zu Rons Kommentar wurde Google-Sammlungen vor einiger Zeit in Guave umbenannt.
Kevin Bourrillion
3
Sie sollten diese Antwort aktualisieren, um zu berücksichtigen, dass Java 8 eine String.join()Methode einführt .
Duncan Jones
46

String.format . Für den Beitritt müssen Sie Ihre eigenen schreiben:

 static String join(Collection<?> s, String delimiter) {
     StringBuilder builder = new StringBuilder();
     Iterator<?> iter = s.iterator();
     while (iter.hasNext()) {
         builder.append(iter.next());
         if (!iter.hasNext()) {
           break;                  
         }
         builder.append(delimiter);
     }
     return builder.toString();
 }

Das Obige stammt von http://snippets.dzone.com/posts/show/91

Allain Lalonde
quelle
6
Genauer gesagt: StringBuffer für jdk1.4 und niedriger, StringBuilder für jdk1.5 und höher, da letzterer nicht synchronisiert ist, daher etwas schneller.
VonC
2
Anstelle von zwei iter.hasNext () - Aufrufen füge ich normalerweise ein Delimeter hinzu und dann "return buf.substring (0, buf.length () - delimeter.length ())".
Vilmantas Baranauskas
2
Sie können es ein wenig rationalisieren, indem Sie vorzeitig beenden, um das Trennzeichen zu vermeiden: while (true) (add_iter; if (! Iter.hasNext ()) break; add_delim;}
13ren
29

Ab Java 8 join()ist es jetzt als zwei Klassenmethoden für die String-Klasse verfügbar. In beiden Fällen ist das erste Argument das Trennzeichen.

Sie können einzelne CharSequences als zusätzliche Argumente übergeben :

String joined = String.join(", ", "Antimony", "Arsenic", "Aluminum", "Selenium");
// "Antimony, Arsenic, Alumninum, Selenium"

Oder Sie können eine übergebenIterable<? extends CharSequence> :

List<String> strings = new LinkedList<String>();
strings.add("EX");
strings.add("TER");
strings.add("MIN");
strings.add("ATE");

String joined = String.join("-", strings);
// "EX-TER-MIN-ATE"

Java 8 fügt außerdem eine neue Klasse hinzu StringJoiner, die Sie folgendermaßen verwenden können:

StringJoiner joiner = new StringJoiner("&");
joiner.add("x=9");
joiner.add("y=5667.7");
joiner.add("z=-33.0");

String joined = joiner.toString();
// "x=9&y=5667.7&z=-33.0"
qntm
quelle
12

Sie können auch variable Argumente für Zeichenfolgen wie folgt verwenden:

  String join (String delim, String ... data) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < data.length; i++) {
      sb.append(data[i]);
      if (i >= data.length-1) {break;}
      sb.append(delim);
    }
    return sb.toString();
  }
sgsweb
quelle
4

Was den Beitritt betrifft, glaube ich, dass dies etwas weniger kompliziert aussehen könnte:

public String join (Collection<String> c) {
    StringBuilder sb=new StringBuilder();
    for(String s: c)
        sb.append(s);
    return sb.toString();
}

Ich kann die Java 5-Syntax nicht so oft verwenden, wie ich möchte (ob Sie es glauben oder nicht, ich habe in letzter Zeit 1.0.x verwendet), daher bin ich vielleicht etwas verrostet, aber ich bin sicher, dass das Konzept korrekt ist .

Zusatz bearbeiten: Das Anhängen von Zeichenfolgen kann langsam sein. Wenn Sie jedoch an GUI-Code oder einer Routine mit kurzer Laufzeit arbeiten, spielt es keine Rolle, ob Sie 0,005 Sekunden oder 0,006 Sekunden benötigen. Wenn Sie also eine Sammlung mit dem Namen "joinMe" hatten. dass Sie an eine vorhandene Zeichenfolge "Ziel" anhängen möchten, wäre es nicht schrecklich, dies einfach zu inline:

for(String s : joinMe)
    target += s;

Es ist ziemlich ineffizient (und eine schlechte Angewohnheit), aber nichts, was Sie wahrnehmen können, es sei denn, es gibt entweder Tausende von Zeichenfolgen oder dies befindet sich in einer großen Schleife oder Ihr Code ist wirklich leistungskritisch.

Noch wichtiger ist, dass es leicht zu merken ist, kurz, schnell und sehr gut lesbar. Leistung ist nicht immer der automatische Gewinner bei der Auswahl des Designs.

Bill K.
quelle
Die for-Schleife ist korrekt, aber Sie sollten sie auch zu einer Collection <String> machen. Wenn Sie dies nicht tun, müssen Sie "für (Objekt o: c)" sagen, da Sie nicht garantieren können, dass alles in c ein String ist.
Michael Myers
Guter Punkt, ich bin noch rostiger mit Generika. Ich werde es in bearbeiten.
Bill K
Beim Inkatieren von Inline: String + String + String verwendet der Compiler StringBuilder, um die Werte anzuhängen. Ich frage mich, ob Sie in der for-loop-Methode dort den zweiten haben, ob der Compiler dasselbe tun würde.
Spencer Kormos
@Spencer K: Es wird zwar ein StringBuilder verwendet, aber für jede Iteration wird ein neuer erstellt (kaum die effizienteste Methode, aber woher soll der Compiler das wissen?). Ich weiß nicht, ob der JIT-Compiler dies zur Laufzeit optimieren könnte.
Michael Myers
2
Oh, Moment mal. Redest du + =? Ja, das ist scheiße. Die Schleife und das Anhängen in meiner Antwort verwenden StringBuilder und das ist es, worüber ich spreche. Obwohl sich + = stark verbessert hat, möchten Sie es wirklich nicht in einer Schleife verwenden - nicht so sehr, dass es fehlerhaft ist, sondern dass es sich wie Mist verhält (Bug ist kein Begriff, der allgemein für Leistungsprobleme verwendet wird - zumindest nicht in meinen 20 Jahren Programmierung). Auch die JIT kann dies immens verbessern - aber darauf würde ich mich nicht verlassen.
Bill K
4

Hier ist eine ziemlich einfache Antwort. Verwenden Sie es, +=da es weniger Code ist, und lassen Sie es vom Optimierer StringBuilderfür Sie in a konvertieren . Mit dieser Methode müssen Sie in Ihrer Schleife keine "is last" -Prüfungen durchführen (Leistungsverbesserung), und Sie müssen sich am Ende keine Gedanken darüber machen, Trennzeichen zu entfernen.

        Iterator<String> iter = args.iterator();
        output += iter.hasNext() ? iter.next() : "";
        while (iter.hasNext()) {
            output += "," + iter.next();
        }
Jess
quelle
1
Sehr elegante Lösung ohne Bibliotheken von Drittanbietern!
Denis Itskovich
2

Ich wollte nicht eine ganze Apache-Bibliothek importieren, um eine einfache Verknüpfungsfunktion hinzuzufügen. Hier ist mein Hack.

    public String join(String delim, List<String> destinations) {
        StringBuilder sb = new StringBuilder();
        int delimLength = delim.length();

        for (String s: destinations) {
            sb.append(s);
            sb.append(delim);
        }

        // we have appended the delimiter to the end 
        // in the previous for-loop. Let's now remove it.
        if (sb.length() >= delimLength) {
            return sb.substring(0, sb.length() - delimLength);
        } else {
            return sb.toString();
        }
    }
Martin Konecny
quelle
@ MrSnowflake Hat der Standard-String-Builder eine Methode "removeCharAt (int index)"? Sie wird nicht in der Version
angezeigt,
@NSjonas - ja seine Bearbeitung meiner Antwort ist falsch. Das removeCharAtexistiert nicht und die gesamte Funktion gibt keinen String mehr zurück ... Behebt dies.
Martin Konecny
während Sie gerade dabei sind ... diese aktuelle Lösung wird eine "Index außerhalb der Grenzen Ausnahme
auslösen,
hat eine Bearbeitung vorgenommen, um die richtige Länge zu erreichen, wenn die Begrenzung mehr als 1 Zeichen beträgt. Es wurde auch ein Problem behoben, bei dem Sie das letzte Zeichen und nicht das erste Zeichen
abgeschnitten haben,
Danke für das Feedback. Aktualisiert.
Martin Konecny
1

Wenn Sie mehrere Zeichenfolgen zu einer verknüpfen (verketten) möchten, sollten Sie einen StringBuilder verwenden. Es ist weitaus besser als zu verwenden

for(String s : joinMe)
    target += s;

Es gibt auch einen leichten Leistungsgewinn gegenüber StringBuffer, da StringBuilder keine Synchronisation verwendet.

Für eine Allzweck-Dienstprogrammmethode wie diese wird sie (möglicherweise) in vielen Situationen mehrmals aufgerufen. Sie sollten sie daher effizient gestalten und nicht viele vorübergehende Objekte zuweisen. Wir haben viele, viele verschiedene Java-Apps profiliert und stellen fast immer fest, dass die Verkettung von Zeichenfolgen und die Zuweisung von Zeichenfolgen / Zeichen [] viel Zeit / Speicher beanspruchen.

Unsere wiederverwendbare Sammlung -> Zeichenfolgenmethode berechnet zuerst die Größe des erforderlichen Ergebnisses und erstellt dann einen StringBuilder mit dieser Anfangsgröße. Dies vermeidet unnötiges Verdoppeln / Kopieren des internen Zeichens [], das beim Anhängen von Zeichenfolgen verwendet wird.

djb
quelle
re: Größe vorberechnen: Wenn das funktioniert, ist es großartig. Das ist nicht immer möglich. Ich erinnere mich, dass ich irgendwo gelesen habe, dass das Verdoppeln der Puffergröße jedes Mal (oder das Multiplizieren mit einem festen Faktor) so etwas wie n log n Leistung ergibt, was eigentlich gar nicht so schlecht ist. Im Allgemeinen besteht die Alternative darin, eine Liste aller Zeichenfolgen zu erstellen, die Sie verketten möchten. Wenn Sie eine ArrayList verwenden, müssen Sie erneut kopieren (es sei denn, Sie kennen die Länge Ihrer Sequenz im Voraus). Wenn Sie LinkedList verwenden, wird mehr RAM- und Garbage-Collection mit den Knotenobjekten verwendet. Manchmal kann man nicht gewinnen. Versuchen Sie es!
Ian
Bei den obigen Beispielen / Anfragen wurde davon ausgegangen, dass eine geordnete Sammlung oder ein Array von Strings verbunden werden soll. Durch die Vorberechnung der Größe vermeiden Sie außerdem zusätzliche nicht verwendete Zeichen, zu denen das interne Wachstum des char [] -Arrays normalerweise führt.
djb
1

Ich schrieb selbst:

public static String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<String> iter = col.iterator();
    if (iter.hasNext())
        sb.append(iter.next().toString());
    while (iter.hasNext()) {
        sb.append(delim);
        sb.append(iter.next().toString());
    }
    return sb.toString();
}

aber Collectionnicht von JSP unterstützt, so dass für Tag - Funktion schrieb ich:

public static String join(List<?> list, String delim) {
    int len = list.size();
    if (len == 0)
        return "";
    StringBuilder sb = new StringBuilder(list.get(0).toString());
    for (int i = 1; i < len; i++) {
        sb.append(delim);
        sb.append(list.get(i).toString());
    }
    return sb.toString();
}

und in die .tldDatei legen :

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
    <function>
        <name>join</name>
        <function-class>com.core.util.ReportUtil</function-class>
        <function-signature>java.lang.String join(java.util.List, java.lang.String)</function-signature>
    </function>
</taglib>

und verwenden Sie es in JSP-Dateien als:

<%@taglib prefix="funnyFmt" uri="tag:com.core.util,2013:funnyFmt"%>
${funnyFmt:join(books, ", ")}
Gavenkoa
quelle
0

Ich sehe hier viele übermäßig komplexe Implementierungen von String.Join. Wenn Sie nicht über Java 1.8 verfügen und keine neue Bibliothek importieren möchten, sollte die folgende Implementierung ausreichen.

public String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    for ( String s : col ) {
        if ( sb.length() != 0 ) sb.append(delim);
        sb.append(s);
    }
    return sb.toString();
}
Brandon Dutton
quelle
-1
ArrayList<Double> j=new ArrayList<>; 
j.add(1);
j.add(.92);
j.add(3); 
String ntop=j.toString(); //ntop= "[1, 0.92, 3]" 

Im Grunde speichert der String ntop den Wert der gesamten Sammlung mit Komma-Trennzeichen und Klammern.

Edward Karak
quelle
1
Ich bin nicht sicher, welchen Teil der Frage dies beantwortet? Es ist kein String.format, es sei denn, Sie planen, die Bits des Strings Stück für Stück zum Array hinzuzufügen, und [1,0.92,3] ist nicht so vielseitig wie ein generischer String .join.
Omar Kooheji
-8

Ich würde nur den String-Verkettungsoperator "+" verwenden, um zwei Strings zu verbinden. s1 += s2;

Amir Bashir
quelle
Weil es schlecht und langsam ist.
MrSnowflake