Der schnellste Weg, um den Inhalt von Set <String> in einen einzelnen String mit durch ein Leerzeichen getrennten Wörtern zu setzen?

75

Ich habe ein paar Set<String>s und möchte jedes davon in ein einzelnes verwandeln, Stringwobei jedes Element des Originals Setdurch ein Leerzeichen "" getrennt ist. Ein naiver erster Ansatz macht es so

Set<String> set_1;
Set<String> set_2;

StringBuilder builder = new StringBuilder();
for (String str : set_1) {
  builder.append(str).append(" ");
}

this.string_1 = builder.toString();

builder = new StringBuilder();
for (String str : set_2) {
  builder.append(str).append(" ");
}

this.string_2 = builder.toString();

Kann sich jemand einen schnelleren, schöneren oder effizienteren Weg vorstellen, dies zu tun?

Lars Andren
quelle

Antworten:

140

Mit commons / lang können Sie dies mit StringUtils.join tun :

String str_1 = StringUtils.join(set_1, " ");

Das kann man der Kürze halber nicht wirklich übertreffen.

Aktualisieren:

Wenn ich diese Antwort noch einmal lese, würde ich jetzt die andere Antwort in Bezug auf Guavas Tischler vorziehen . Tatsächlich gehe ich heutzutage nicht in die Nähe von Apache Commons.

Ein weiteres Update:

Java 8 führte die Methode ein String.join()

String joined = String.join(",", set);

Dies ist zwar nicht so flexibel wie die Guava-Version, aber praktisch, wenn Sie die Guava-Bibliothek nicht in Ihrem Klassenpfad haben.

Sean Patrick Floyd
quelle
2
kurz, aber unflexibel. Ich ersetze "" durch null, ob ich es will oder nicht, und habe keine Option zum Überspringen von Nullen ... aus diesem Grund haben wir Joiner für Guava erstellt (siehe andere Antwort).
Kevin Bourrillion
Normalerweise habe ich keine Nullen in meinen Sammlungen, daher ist der kurze Ansatz für mich in Ordnung, aber Guave rockt! Vielen Dank, dass Sie das möglich gemacht haben ...
Sean Patrick Floyd
Da Sie nulls nicht erwarten, dann würden Sie Ihre Schreiner wie die Luft zu sprengen , wenn Sie haben eine Null - das ist, was Guava ist standardmäßig der Joiner der Fall ist. :-)
Kevin Bourrillion
1
@ smp7d noch sollte es sein. Null ist keine gültige Eingabe. Angenommen, es wäre gefährlich. Wenn Sie Nullwerte mit Vorbedingungsprüfungen ablehnen, müssen Sie sich nie wieder um die Nullsicherheit sorgen
Sean Patrick Floyd
1
@ SeanPatrickFloyd Hmm, interessant aussehende Lektüre, danke für den Hinweis. Ich bin sehr vorsichtig, absichtlich und minimal mit meiner Verwendung von Null im Allgemeinen, also habe ich vielleicht nicht so viel bemerkt. Am wichtigsten ist jedoch, dass ich vor der Verwendung immer IMMER Werte / Parameter auf Null überprüfe und in fast allen Fällen eine Ausnahme
auslöse,
31

Wenn Sie Java 8 verwenden, können Sie das native verwenden

String.join(CharSequence delimiter, Iterable<? extends CharSequence> elements)

Methode:

Gibt eine neue Stringzusammengesetzte Kopie der CharSequenceElemente zurück, die zusammen mit einer Kopie des angegebenen Trennzeichens verbunden sind. Zum Beispiel:

 Set<String> strings = new LinkedHashSet<>();
 strings.add("Java"); strings.add("is");
 strings.add("very"); strings.add("cool");
 String message = String.join("-", strings);
 //message returned is: "Java-is-very-cool"

SetGeräte Iterable, also einfach verwenden:

String.join(" ", set_1);
Jasper de Vries
quelle
20

Als Kontrapunkt zu Seanizers Antwort auf Commons-Lang würden Sie Joiner verwenden , wenn Sie die Guava-Bibliotheken von Google verwenden (die ich in vielerlei Hinsicht als "Nachfolger" von Commons-Lang betrachten würde) :

Joiner.on(" ").join(set_1);

mit dem Vorteil einiger Hilfsmethoden, um Dinge wie:

Joiner.on(" ").skipNulls().join(set_1);
// If 2nd item was null, would produce "1, 3"

oder

Joiner.on(" ").useForNull("<unknown>").join(set_1);
// If 2nd item was null, would produce "1, <unknown>, 3"

Es unterstützt auch das direkte Anhängen an StringBuilders and Writers und andere solche Feinheiten.

Cowan
quelle
Was ist der Unterschied zwischen Guavenbibliotheken und Google-Sammlungsbibliothek?
Shervin Asgari
Der Hauptunterschied besteht darin, dass Guave sich mit Generika und Commons / Sammlungen nicht auskennt, aber abgesehen davon: Es handelt sich um zwei verschiedene Bibliotheken, die von zwei verschiedenen Teams geschrieben wurden und einige ähnliche Probleme (und einige nicht ähnliche) mit unterschiedlichen Ansätzen lösen
Sean Patrick Floyd
2
@ Seanizer, Shervin fragte nach Guave gegen Google-Sammlungen, nicht nach Guave gegen Commons :) Shervin - Guave ist einfach der Ersatz für Google-Sammlungen. Mit zunehmendem Umfang des Projekts beschränkte es sich nicht mehr nur auf Sammlungen, sodass eine Namensänderung angebracht war. Google-Sammlungen sollten grundsätzlich als veraltet angesehen werden. Guave ist ein Ersatz für Fehlerbehebungen und weitere Funktionen.
Cowan
7

Ich habe die StringUtil-Bibliothek nicht verfügbar (ich habe keine andere Wahl), also habe ich mir mit Standard-Java diese ausgedacht.

Wenn Sie sicher sind, dass Ihre festgelegten Daten keine Kommas oder eckigen Klammern enthalten, können Sie Folgendes verwenden:

mySet.toString().replaceAll("\\[|\\]","").replaceAll(","," ");

Eine Menge von "a", "b", "c" wird über .toString () in eine Zeichenfolge "[a, b, c]" konvertiert.

Ersetzen Sie dann die zusätzliche Interpunktion nach Bedarf.

Schmutz.

user2808054
quelle
2
Nett! Immer cool zu sehen, was Sie mit den JDK-Standardbibliotheken machen können :)
Lars Andren
sauber, einfach, nicht von Bibliotheken abhängig, daher nicht zum Gewicht der App hinzufügen - ich liebe es!
Lukas Novicky
1
... solange du natürlich keine "[" oder "]" oder "," in deinem Set hast;)
Tedil
Ich hatte bereits die Kommas erwähnt, aber Sie haben auch Recht mit [und]. Die Antwort wurde aktualisiert.
user2808054
1
Einige Leute sind leider ohne Java8 + und haben keinen einfachen Zugang zu Bibliotheken. Antworten wie diese sind für diese Leute super hilfreich.
Lewdev
6

Vielleicht eine kürzere Lösung:

public String test78 (Set<String> set) {
    return set
        .stream()
        .collect(Collectors.joining(" "));
}

oder

public String test77 (Set<String> set) {
    return set
        .stream()
        .reduce("", (a,b)->(a + " " + b));
}

aber einheimisch, definitiv schneller

public String test76 (Set<String> set) {
    return String.join(" ", set);
}
Alexey Rykhalskiy
quelle
4

Ich benutze diese Methode:

public static String join(Set<String> set, String sep) {
    String result = null;
    if(set != null) {
        StringBuilder sb = new StringBuilder();
        Iterator<String> it = set.iterator();
        if(it.hasNext()) {
            sb.append(it.next());
        }
        while(it.hasNext()) {
            sb.append(sep).append(it.next());
        }
        result = sb.toString();
    }
    return result;
}
tuxdna
quelle
2

Ich bin verwirrt über die Codereplikation. Warum nicht eine Funktion berücksichtigen, die einen Satz benötigt und eine Zeichenfolge zurückgibt?

Abgesehen davon bin ich mir nicht sicher, ob Sie noch viel tun können, außer vielleicht dem Stringbuilder einen Hinweis auf die erwartete Kapazität zu geben (wenn Sie diese anhand der festgelegten Größe und der angemessenen Erwartung der Stringlänge berechnen können).

Auch dafür gibt es Bibliotheksfunktionen, aber ich bezweifle, dass sie wesentlich effizienter sind.

Uri
quelle
Danke für deine Antwort! Ich habe es nicht in eine separate Funktion eingefügt, um die Tatsache hervorzuheben, dass ich versuche, die 'Builder'-Variable wiederzuverwenden. Vielleicht spielt das keine Rolle?
Lars Andren
3
Es spielt keine Rolle. Das erneute Verwenden von Variablen kann Sie später in Schwierigkeiten bringen. Lassen Sie stattdessen nur Variablen den Gültigkeitsbereich verlassen, wenn sie nicht mehr benötigt werden.
Gunslinger47
@ Gunslinger, ich verstehe, danke! @Uri, danke für die ersten Kapazitätstipps.
Lars Andren
1
@Lars, es gibt Fälle, in denen die Wiederverwendung sinnvoll ist, in anderen ist es jedoch besser, sie zu sichern und von vorne zu beginnen. Ich bin mir nicht sicher, was hier besser ist. Sie können eine Dienstprogrammklasse mit einem gemeinsam genutzten Instanzvariablen-Builder schreiben. Ein Vorteil für einen Builder pro Lauf besteht darin, dass Sie eine große Anzahl von Sätzen parallel zu mehreren Threads konvertieren können.
Uri
2

Dies kann erreicht werden, indem ein Stream aus dem Satz erstellt und die Elemente dann mithilfe einer Reduzierungsoperation wie unten gezeigt kombiniert werden (weitere Informationen zu Java 8-Streams finden Sie hier ):

Optional<String> joinedString = set1.stream().reduce(new 
BinaryOperator<String>() {

     @Override
     public String apply(String t, String u) {

       return t + " " + u;
    }
});
return joinedString.orElse("");
Ehab Qadah
quelle