Java: Konvertiere List <String> in einen String

595

JavaScript hat Array.join()

js>["Bill","Bob","Steve"].join(" and ")
Bill and Bob and Steve

Hat Java so etwas? Ich weiß, dass ich mit StringBuilder selbst etwas aufbauen kann:

static public String join(List<String> list, String conjunction)
{
   StringBuilder sb = new StringBuilder();
   boolean first = true;
   for (String item : list)
   {
      if (first)
         first = false;
      else
         sb.append(conjunction);
      sb.append(item);
   }
   return sb.toString();
}

... aber es macht keinen Sinn, dies zu tun, wenn so etwas bereits Teil des JDK ist.

Jason S.
quelle
1
Siehe auch diese Frage für Listen
Casebash
18
Nicht streng verwandt, aber Android hat eine eingebaute Join-Funktion als Teil ihrer TextUtils-Klasse: developer.android.com/reference/android/text/… , java.lang.Iterable)
Xavi
16
Java 8 hat eine String.join()Methode. Werfen Sie einen Blick auf diese Antwort , wenn Sie mit Java 8 (oder neuer) stackoverflow.com/a/22577565/1115554
micha

Antworten:

763

Mit Java 8 können Sie dies ohne eine Bibliothek eines Drittanbieters tun.

Wenn Sie einer Sammlung von Zeichenfolgen beitreten möchten, können Sie die neue Methode String.join () verwenden:

List<String> list = Arrays.asList("foo", "bar", "baz");
String joined = String.join(" and ", list); // "foo and bar and baz"

Wenn Sie eine Sammlung mit einem anderen Typ als String haben, können Sie die Stream-API mit dem beitretenden Kollektor verwenden :

List<Person> list = Arrays.asList(
  new Person("John", "Smith"),
  new Person("Anna", "Martinez"),
  new Person("Paul", "Watson ")
);

String joinedFirstNames = list.stream()
  .map(Person::getFirstName)
  .collect(Collectors.joining(", ")); // "John, Anna, Paul"

Die StringJoinerKlasse kann auch nützlich sein.

micha
quelle
7
Leider akzeptiert String.join nur CharSequences und nicht so, wie man es sich erhofft. Nicht einmal sicher, ob es null sicher ist
Marc
@ MarcassertThat(String.join(", ", Lists.newArrayList("1", null)), is("1, null"));
Sanghyun Lee
StringJoiner ist besonders nützlich, wenn man sich mit etwas wie dem [a, b, c]Einschließen der Klammern verbinden möchte .
Koppor
@Marc String.join akzeptiert auch Iterable<CharSequence>; Schnittstellenbeziehung:Iterable -> Collection -> List
Aff
306

Alle Verweise auf Apache Commons sind in Ordnung (und das verwenden die meisten Leute), aber ich denke, das Guava- Äquivalent Joiner hat eine viel schönere API.

Sie können den einfachen Join-Fall mit ausführen

Joiner.on(" and ").join(names)

aber auch leicht mit Nullen umgehen:

Joiner.on(" and ").skipNulls().join(names);

oder

Joiner.on(" and ").useForNull("[unknown]").join(names);

und (nützlich genug für mich, um es gegenüber Commons-Lang zu bevorzugen), die Fähigkeit, mit Karten umzugehen:

Map<String, Integer> ages = .....;
String foo = Joiner.on(", ").withKeyValueSeparator(" is ").join(ages);
// Outputs:
// Bill is 25, Joe is 30, Betty is 35

Dies ist äußerst nützlich für das Debuggen usw.

Cowan
quelle
10
danke für den stecker! Unser Joiner bietet Ihnen auch die Möglichkeit, direkt an ein Appendable (z. B. einen StringBuilder oder einen Writer) anzuhängen, ohne Zwischenzeichenfolgen zu erstellen, die der Apache-Bibliothek anscheinend fehlen.
Kevin Bourrillion
2
Das ist großartig, aber können Sie bitte eine .useForLastSeparator()oder eine ähnliche Methode hinzufügen ? Auf diese Weise könnten Sie so etwas wie "und" nur zwischen den letzten beiden Elementen erhalten (mit "," für den Rest).
Jeff Evans
2
Ja, es ist threadsicher. Der einzige in einem Joiner gespeicherte Status ist der separator(der ist final). Alles, was ich in Guava gesehen habe, wo ich früher ein Apache Commons-Äquivalent verwendet habe, war in Guava so viel besser (sprich: sauberer, schneller, sicherer und im Allgemeinen nur robuster), mit weniger Edge-Case-Fehlern und Thread-Sicherheitsproblemen und kleinerer Speicherbedarf. Ihr "bestmöglicher" Leitfaden scheint bisher zuzutreffen.
Shadow Man
Wenn Sie in die entgegengesetzte Richtung gehen müssen (dh einen String in Teile teilen), sehen Sie sich den Splitter der Guava-Klasse an - ebenfalls sehr gut entworfen / implementiert.
Matt Passell
In Java 8 gibt es eine String.join()Methode und eine StringJoinerKlasse.
TheTechnoKid
138

Nicht sofort einsatzbereit, aber viele Bibliotheken haben ähnliche Eigenschaften:

Commons Lang:

org.apache.commons.lang.StringUtils.join(list, conjunction);

Frühling:

org.springframework.util.StringUtils.collectionToDelimitedString(list, conjunction);
Arne Burmeister
quelle
125

Unter Android können Sie die TextUtils- Klasse verwenden.

TextUtils.join(" and ", names);
Rafal Enden
quelle
11
Ich habe danach gesucht. String.joinerfordert min api level 26 auf android.
Karakose
49

Nein, in der Standard-Java-API gibt es keine solche Komfortmethode.

Es überrascht nicht, dass Apache Commons so etwas in seiner StringUtils-Klasse bereitstellt, falls Sie es nicht selbst schreiben möchten.

Bart Kiers
quelle
11
Es hat mich immer gestört, dass String einen Split hat, aber keinen Join. In gewisser Weise macht es Sinn, aber es ist nur ärgerlich, dass es in String nicht mindestens eine statische Methode dafür gibt.
Powerlord
3
Ich bin bei dir Bemrose. Zumindest gaben sie uns eine isEmpty()Methode anstelle einer (statischen) join()Methode in String...: rollseyes: :)
Bart Kiers
41

Drei Möglichkeiten in Java 8:

List<String> list = Arrays.asList("Alice", "Bob", "Charlie")

String result = String.join(" and ", list);

result = list.stream().collect(Collectors.joining(" and "));

result = list.stream().reduce((t, u) -> t + " and " + u).orElse("");
amra
quelle
27

Mit einem Java 8-Kollektor kann dies mit dem folgenden Code durchgeführt werden:

Arrays.asList("Bill", "Bob", "Steve").stream()
.collect(Collectors.joining(" and "));

Auch die einfachste Lösung in Java 8:

String.join(" and ", "Bill", "Bob", "Steve");

oder

String.join(" and ", Arrays.asList("Bill", "Bob", "Steve"));
aifa
quelle
21

Ich habe dieses geschrieben (ich benutze es für Bohnen und Exploits toString, also schreibe nicht Collection<String>):

public static String join(Collection<?> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<?> 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 TLD 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
1
vorschlagen Wechsel Collection<>zu Iterable<>?
Jason S
@ Jason +1. Guter Punkt, aber lesen Sie stackoverflow.com/questions/1159797/…
gavenkoa
19

Code, den Sie haben, ist der richtige Weg, wenn Sie JDK ohne externe Bibliotheken verwenden möchten. Es gibt keinen einfachen "Einzeiler", den Sie in JDK verwenden könnten.

Wenn Sie externe Bibliotheken verwenden können, empfehlen wir Ihnen, die org.apache.commons.lang.StringUtils- Klasse in der Apache Commons-Bibliothek zu überprüfen .

Ein Anwendungsbeispiel:

List<String> list = Arrays.asList("Bill", "Bob", "Steve");
String joinedResult = StringUtils.join(list, " and ");
Juha Syrjälä
quelle
17

Ein orthodoxer Weg, dies zu erreichen, besteht darin, eine neue Funktion zu definieren:

public static String join(String joinStr, String... strings) {
    if (strings == null || strings.length == 0) {
        return "";
    } else if (strings.length == 1) {
        return strings[0];
    } else {
        StringBuilder sb = new StringBuilder(strings.length * 1 + strings[0].length());
        sb.append(strings[0]);
        for (int i = 1; i < strings.length; i++) {
            sb.append(joinStr).append(strings[i]);
        }
        return sb.toString();
    }
}

Stichprobe:

String[] array = new String[] { "7, 7, 7", "Bill", "Bob", "Steve",
        "[Bill]", "1,2,3", "Apple ][","~,~" };

String joined;
joined = join(" and ","7, 7, 7", "Bill", "Bob", "Steve", "[Bill]", "1,2,3", "Apple ][","~,~");
joined = join(" and ", array); // same result

System.out.println(joined);

Ausgabe:

7, 7, 7 und Bill und Bob und Steve und [Bill] und 1,2,3 und Apple] [und ~, ~

Daniel De León
quelle
5
Es ist wahrscheinlich nicht die beste Idee, einem Methodenparameter den gleichen Namen wie der Methode selbst zu geben. separatorwäre viel suggestiver.
ccpizza
10

Java 8 Lösung mit java.util.StringJoiner

Java 8 hat eine StringJoinerKlasse. Aber Sie müssen noch ein bisschen Boilerplate schreiben, weil es Java ist.

StringJoiner sj = new StringJoiner(" and ", "" , "");
String[] names = {"Bill", "Bob", "Steve"};
for (String name : names) {
   sj.add(name);
}
System.out.println(sj);
klingt.net
quelle
Sie müssen kein bisschen Boilerplate schreiben, wenn Sie die bequemere Methode String.join () verwenden .
Andy Thomas
9

Sie können die Apache Commons-Bibliothek verwenden, die eine StringUtils-Klasse und eine Join-Methode enthält.

Überprüfen Sie diesen Link: https://commons.apache.org/proper/commons-lang/javadocs/api.2.0/org/apache/commons/lang/StringUtils.html

Beachten Sie, dass der obige Link mit der Zeit veraltet sein kann. In diesem Fall können Sie einfach im Web nach "apache commons StringUtils" suchen, um die neueste Referenz zu finden.

(in diesem Thread referenziert) Java-Entsprechungen von C # String.Format () und String.Join ()

dcp
quelle
7

Du kannst das:

String aToString = java.util.Arrays.toString(anArray);
// Do not need to do this if you are OK with '[' and ']'
aToString = aToString.substring(1, aToString.length() - 1);

Oder ein Einzeiler (nur wenn Sie nicht '[' und ']' wollen)

String aToString = java.util.Arrays.toString(anArray).substring(1).replaceAll("\\]$", "");

Hoffe das hilft.

NawaMan
quelle
1
Dies wird die Konjunktion der Wahl nicht anhängen.
Hexium
HOPPLA!! Es tut mir leid, das habe ich nicht bemerkt.
NawaMan
6

Eine unterhaltsame Möglichkeit, dies mit reinem JDK in einer einzigen Dienstzeile zu tun:

String[] array = new String[] { "Bill", "Bob", "Steve","[Bill]","1,2,3","Apple ][" };
String join = " and ";

String joined = Arrays.toString(array).replaceAll(", ", join)
        .replaceAll("(^\\[)|(\\]$)", "");

System.out.println(joined);

Ausgabe:

Bill und Bob und Steve und [Bill] und 1,2,3 und Apple] [


Eine nicht zu perfekte und nicht zu lustige Art!

String[] array = new String[] { "7, 7, 7","Bill", "Bob", "Steve", "[Bill]",
        "1,2,3", "Apple ][" };
String join = " and ";

for (int i = 0; i < array.length; i++) array[i] = array[i].replaceAll(", ", "~,~");
String joined = Arrays.toString(array).replaceAll(", ", join)
        .replaceAll("(^\\[)|(\\]$)", "").replaceAll("~,~", ", ");

System.out.println(joined);

Ausgabe:

7, 7, 7 und Bill und Bob und Steve und [Bill] und 1,2,3 und Apple] [

Daniel De León
quelle
Versuchen Sie es mit "[Bill]", "1,2,3" und "Apple] [". Kreativ, aber es gibt Fälle, in denen es falsch ist.
Jason S
1
Versuchen Sie es jetzt mit "~, ~". Sie können ein Programm mit dieser Art von Architektur nicht vollständig kugelsicher machen.
Jason S
Ja, ich weiß ... ich habe die Ablehnung entfernt. Sie sollten jedoch etwas genauer überlegen, ob Sie hier Antworten veröffentlichen möchten, insbesondere bei Fragen wie dieser, die mehrere Jahre alt sind. Die Antworten sind nicht nur sofort für das Originalposter nützlich, sondern werden auch in Google-Suchanfragen angezeigt. (Bei Fragen, die mehrere Jahre alt sind, haben neue Antworten möglicherweise keinen Wert für das Originalplakat.) Lösungen mit Nachteilen oder Fehlern können für Personen schädlich sein, die sie später außerhalb des Kontexts finden.
Jason S
1
Ich lerne hier viel von anderen Posts wie meinem, mit nicht perfekter Lösung, aber wirklich reich an Wissen und Nebendenken. Entspannen Sie sich für andere, jeder muss verstehen, was jede Zeile seines Codes tut. Denken Sie daran, Programmieren ist wie eine Kunst, und selbst jede Codezeile bedeutet etwas über die Persönlichkeit des Programmierers. Und ja, ich werde diesen Code nicht in der Produktion verwenden!
Daniel De León
4

Wenn Sie Eclipse-Sammlungen (früher GS-Sammlungen ) verwenden, können Sie die makeString()Methode verwenden.

List<String> list = Arrays.asList("Bill", "Bob", "Steve");

String string = ListAdapter.adapt(list).makeString(" and ");

Assert.assertEquals("Bill and Bob and Steve", string);

Wenn Sie Ihren Listin einen Eclipse-Sammlungstyp konvertieren können, können Sie den Adapter entfernen.

MutableList<String> list = Lists.mutable.with("Bill", "Bob", "Steve");
String string = list.makeString(" and ");

Wenn Sie nur eine durch Kommas getrennte Zeichenfolge möchten, können Sie die Version verwenden makeString(), die keine Parameter akzeptiert.

Assert.assertEquals(
    "Bill, Bob, Steve", 
    Lists.mutable.with("Bill", "Bob", "Steve").makeString());

Hinweis: Ich bin ein Committer für Eclipse-Sammlungen.

Craig P. Motlin
quelle
3

Mit Java 1.8 Stream kann verwendet werden,

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
List<String> list = Arrays.asList("Bill","Bob","Steve").
String str = list.stream().collect(Collectors.joining(" and "));
Saurabh
quelle
3

BEARBEITEN

Ich bemerke auch das toString()zugrunde liegende Implementierungsproblem und das Element, das das Trennzeichen enthält, aber ich dachte, ich wäre paranoid.

Da ich diesbezüglich zwei Kommentare habe, ändere ich meine Antwort auf:

static String join( List<String> list , String replacement  ) {
    StringBuilder b = new StringBuilder();
    for( String item: list ) { 
        b.append( replacement ).append( item );
    }
    return b.toString().substring( replacement.length() );
}

Welches sieht ziemlich ähnlich aus wie die ursprüngliche Frage.

Wenn Sie also nicht das ganze Glas zu Ihrem Projekt hinzufügen möchten, können Sie dies verwenden.

Ich denke, an Ihrem Originalcode ist nichts auszusetzen. Tatsächlich sieht die Alternative, die jeder vorschlägt, fast gleich aus (obwohl sie eine Reihe zusätzlicher Validierungen durchführt).

Hier ist es, zusammen mit der Apache 2.0-Lizenz.

public static String join(Iterator iterator, String separator) {
    // handle null, zero and one elements before building a buffer
    if (iterator == null) {
        return null;
    }
    if (!iterator.hasNext()) {
        return EMPTY;
    }
    Object first = iterator.next();
    if (!iterator.hasNext()) {
        return ObjectUtils.toString(first);
    }

    // two or more elements
    StringBuffer buf = new StringBuffer(256); // Java default is 16, probably too small
    if (first != null) {
        buf.append(first);
    }

    while (iterator.hasNext()) {
        if (separator != null) {
            buf.append(separator);
        }
        Object obj = iterator.next();
        if (obj != null) {
            buf.append(obj);
        }
    }
    return buf.toString();
}

Jetzt wissen wir, danke Open Source

OscarRyz
quelle
Dies wird jetzt funktionieren, aber Sie können nicht sicher sein, wie sich List.toString () in Zukunft verhalten wird. Ich würde niemals einer anderen toString () -Operation vertrauen, als eine Informationszeichenfolge über das betreffende Objekt zu drucken. Zu Ihrer Verteidigung sind Sie nicht der einzige, der diese Lösung vorschlägt.
Hans Doggen
Was ist, wenn der Inhalt eines Elements in der Liste den Teilstring enthält ", "?
Bart Kiers
@Hans & @Bart: Du hast recht. Ich dachte, niemand anderes wird es bemerken: P Ich ändere meine Antwort.
OscarRyz
Dies hängt vom Anwendungsfall ab. Für Debugging-Zwecke ist toString () IMO in Ordnung ... vorausgesetzt, Sie verlassen sich nicht auf eine bestimmte Formatierung. Und trotzdem werden zukünftige Änderungen abgefangen, wenn Sie einen Test für Ihren Code schreiben.
Akostadinov
2

Die Guava-API von Google hat auch .join (), obwohl Apache Commons (wie bei den anderen Antworten offensichtlich sein sollte) hier so ziemlich der Standard ist.

Dean J.
quelle
1

Java 8 bringt das

Collectors.joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)

Methode, die mit null null sicher ist prefix + suffix für Nullwerte verwendet wird.

Es kann auf folgende Weise verwendet werden:

String s = stringList.stream().collect(Collectors.joining(" and ", "prefix_", "_suffix"))

Die Collectors.joining(CharSequence delimiter)Methode ruft nur joining(delimiter, "", "")intern auf.

thg
quelle
0

Sie können dies in den StringUtils von Spring Framework verwenden. Ich weiß, dass es bereits erwähnt wurde, aber Sie können diesen Code einfach nehmen und er funktioniert sofort, ohne dass Spring dafür benötigt wird.

// from https://github.com/spring-projects/spring-framework/blob/master/spring-core/src/main/java/org/springframework/util/StringUtils.java

/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
public class StringUtils {
    public static String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix) {
        if(coll == null || coll.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        Iterator<?> it = coll.iterator();
        while (it.hasNext()) {
            sb.append(prefix).append(it.next()).append(suffix);
            if (it.hasNext()) {
                sb.append(delim);
            }
        }
        return sb.toString();
    }
}
EpicPandaForce
quelle
-3

Versuche dies:

java.util.Arrays.toString(anArray).replaceAll(", ", ",")
                .replaceFirst("^\\[","").replaceFirst("\\]$","");
Jorge
quelle
1
Wahrscheinlich, weil die Frage die Verwendung einer Liste im Vergleich zu einem Array erfordert? Achselzucken
Nick Coelius
4
Denn wenn Sie ein "," in Ihren Zeichenfolgen haben, funktioniert es nicht.
Cyrille Pontvieux