it-swarm-eu.dev

Was ist der beste Weg, um in Java eine Zeichenfolge von durch Trennzeichen getrennten Elementen zu erstellen?

Während ich in einer Java App arbeitete, musste ich kürzlich eine durch Kommas getrennte Liste von Werten zusammenstellen, um sie an einen anderen Webdienst zu übergeben, ohne vorher zu wissen, wie viele Elemente es geben würde. Das Beste, was ich bekommen konnte Bis auf die Spitze meines Kopfes war so etwas:

public String appendWithDelimiter( String original, String addition, String delimiter ) {
    if ( original.equals( "" ) ) {
        return addition;
    } else {
        return original + delimiter + addition;
    }
}

String parameterString = "";
if ( condition ) parameterString = appendWithDelimiter( parameterString, "elementName", "," );
if ( anotherCondition ) parameterString = appendWithDelimiter( parameterString, "anotherElementName", "," );

Mir ist klar, dass dies nicht besonders effizient ist, da überall Zeichenfolgen erstellt werden, aber ich wollte mehr Klarheit als Optimierung.

In Ruby kann ich stattdessen so etwas tun, was sich viel eleganter anfühlt:

parameterArray = [];
parameterArray << "elementName" if condition;
parameterArray << "anotherElementName" if anotherCondition;
parameterString = parameterArray.join(",");

Da es in Java keinen Join-Befehl gibt, konnte ich nichts Vergleichbares herausfinden.

Also, was ist der beste Weg, um dies in Java zu tun?

292
Sean McMains

Pre Java 8:

Apache's commons lang ist dein Freund hier - es bietet eine Join-Methode, die derjenigen, auf die du in Ruby Bezug nimmst, sehr ähnlich ist:

StringUtils.join(Java.lang.Iterable,char)


Java 8:

Java 8 bietet eine sofort einsatzbereite Verbindung über StringJoiner und String.join(). Die folgenden Ausschnitte zeigen, wie Sie sie verwenden können:

StringJoiner

StringJoiner joiner = new StringJoiner(",");
joiner.add("01").add("02").add("03");
String joinedString = joiner.toString(); // "01,02,03"

String.join(CharSequence delimiter, CharSequence... elements))

String joinedString = String.join(" - ", "04", "05", "06"); // "04 - 05 - 06"

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

List<String> strings = new LinkedList<>();
strings.add("Java");strings.add("is");
strings.add("cool");
String message = String.join(" ", strings);
//message returned is: "Java is cool"
503
Martin Gladdish

Sie könnten eine kleine Join-artige Dienstprogrammmethode schreiben, die auf Java.util.Lists funktioniert

public static String join(List<String> list, String delim) {

    StringBuilder sb = new StringBuilder();

    String loopDelim = "";

    for(String s : list) {

        sb.append(loopDelim);
        sb.append(s);            

        loopDelim = delim;
    }

    return sb.toString();
}

Dann benutze es wie folgt:

    List<String> list = new ArrayList<String>();

    if( condition )        list.add("elementName");
    if( anotherCondition ) list.add("anotherElementName");

    join(list, ",");
52
Rob Dickerson

Im Fall von Android ist die StringUtils-Klasse von commons nicht verfügbar, daher habe ich sie verwendet

Android.text.TextUtils.join(CharSequence delimiter, Iterable tokens)

http://developer.Android.com/reference/Android/text/TextUtils.html

47
Kevin

Die Googles Guava-Bibliothek hat com.google.common.base.Joiner Klasse, die hilft, solche Aufgaben zu lösen.

Proben:

"My pets are: " + Joiner.on(", ").join(Arrays.asList("rabbit", "parrot", "dog")); 
// returns "My pets are: rabbit, parrot, dog"

Joiner.on(" AND ").join(Arrays.asList("field1=1" , "field2=2", "field3=3"));
// returns "field1=1 AND field2=2 AND field3=3"

Joiner.on(",").skipNulls().join(Arrays.asList("London", "Moscow", null, "New York", null, "Paris"));
// returns "London,Moscow,New York,Paris"

Joiner.on(", ").useForNull("Team held a draw").join(Arrays.asList("FC Barcelona", "FC Bayern", null, null, "Chelsea FC", "AC Milan"));
// returns "FC Barcelona, FC Bayern, Team held a draw, Team held a draw, Chelsea FC, AC Milan"

Hier ist ein Artikel über Guavas String-Dienstprogramme .

31
Alex K

In Java 8 können Sie String.join() verwenden:

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

Schauen Sie sich auch diese Antwort für ein Stream-API-Beispiel an.

25
micha

Sie können es verallgemeinern, aber es gibt keine Verbindung in Java, wie Sie gut sagen.

Dies könnte besser funktionieren.

public static String join(Iterable<? extends CharSequence> s, String delimiter) {
    Iterator<? extends CharSequence> iter = s.iterator();
    if (!iter.hasNext()) return "";
    StringBuilder buffer = new StringBuilder(iter.next());
    while (iter.hasNext()) buffer.append(delimiter).append(iter.next());
    return buffer.toString();
}
20
Vinko Vrsalovic

Verwenden Sie einen Ansatz basierend auf Java.lang.StringBuilder ! ("Eine veränderbare Folge von Zeichen.")

Wie Sie bereits erwähnt haben, werden durch all diese Verkettungen von Zeichenfolgen überall Zeichenfolgen erstellt. StringBuilder macht das nicht.

Warum StringBuilder anstelle von StringBuffer ? Aus dem Javadoc StringBuilder:

Nach Möglichkeit wird empfohlen, diese Klasse anstelle von StringBuffer zu verwenden, da sie in den meisten Implementierungen schneller ist.

15
Stu Thompson

in Java 8 können Sie dies wie folgt tun:

list.stream().map(Object::toString)
                        .collect(Collectors.joining(delimiter));

wenn die Liste Nullen enthält, können Sie Folgendes verwenden:

list.stream().map(String::valueOf)
                .collect(Collectors.joining(delimiter))
14
gladiator

Ich würde Google Collections verwenden. Es gibt eine Nice Join-Einrichtung.
http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/base/Join.html

Aber wenn ich es selbst schreiben wollte,

package util;

import Java.util.ArrayList;
import Java.util.Iterable;
import Java.util.Collections;
import Java.util.Iterator;

public class Utils {
    // accept a collection of objects, since all objects have toString()
    public static String join(String delimiter, Iterable<? extends Object> objs) {
        if (objs.isEmpty()) {
            return "";
        }
        Iterator<? extends Object> iter = objs.iterator();
        StringBuilder buffer = new StringBuilder();
        buffer.append(iter.next());
        while (iter.hasNext()) {
            buffer.append(delimiter).append(iter.next());
        }
        return buffer.toString();
    }

    // for convenience
    public static String join(String delimiter, Object... objs) {
        ArrayList<Object> list = new ArrayList<Object>();
        Collections.addAll(list, objs);
        return join(delimiter, list);
    }
}

Ich denke, es funktioniert besser mit einer Objektsammlung, da Sie Ihre Objekte jetzt nicht mehr in Zeichenfolgen konvertieren müssen, bevor Sie sie verbinden.

10
Eric Normand

Apache commons Die StringUtils-Klasse verfügt über eine Join-Methode.

8
Alejandro

Java 8

stringCollection.stream().collect(Collectors.joining(", "));
5
gstackoverflow

Und eine minimale (wenn Sie Apache Commons vs Guava nicht in Projektabhängigkeiten einbeziehen möchten, nur um Strings zusammenzufügen)

/**
 *
 * @param delim : String that should be kept in between the parts
 * @param parts : parts that needs to be joined
 * @return  a String that's formed by joining the parts
 */
private static final String join(String delim, String... parts) {
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < parts.length - 1; i++) {
        builder.append(parts[i]).append(delim);
    }
    if(parts.length > 0){
        builder.append(parts[parts.length - 1]);
    }
    return builder.toString();
}
3
Thamme Gowda

Verwenden Sie StringBuilder und die Klasse Separator

StringBuilder buf = new StringBuilder();
Separator sep = new Separator(", ");
for (String each : list) {
    buf.append(sep).append(each);
}

Trennzeichen umschließt ein Trennzeichen. Das Trennzeichen wird von der toString -Methode von Separator zurückgegeben, es sei denn, beim ersten Aufruf wird die leere Zeichenfolge zurückgegeben!

Quellcode für die Klasse Separator

public class Separator {

    private boolean skipFirst;
    private final String value;

    public Separator() {
        this(", ");
    }

    public Separator(String value) {
        this.value = value;
        this.skipFirst = true;
    }

    public void reset() {
        skipFirst = true;
    }

    public String toString() {
        String sep = skipFirst ? "" : value;
        skipFirst = false;
        return sep;
    }

}
3
akuhn

Hierfür können Sie Javas StringBuilder -Typ verwenden. Es gibt auch StringBuffer, aber es enthält zusätzliche Thread-Sicherheitslogik, die häufig nicht erforderlich ist.

3
Kent Boogaart

Wenn Sie Eclipse Collections verwenden, können Sie makeString() oder appendString() verwenden.

makeString() gibt eine String -Darstellung zurück, ähnlich wie toString().

Es hat drei Formen

  • makeString(start, separator, end)
  • makeString(separator) Standardmäßig beginnen und enden leere Zeichenfolgen
  • makeString() setzt das Trennzeichen standardmäßig auf ", " (Komma und Leerzeichen)

Codebeispiel:

MutableList<Integer> list = FastList.newListWith(1, 2, 3);
assertEquals("[1/2/3]", list.makeString("[", "/", "]"));
assertEquals("1/2/3", list.makeString("/"));
assertEquals("1, 2, 3", list.makeString());
assertEquals(list.toString(), list.makeString("[", ", ", "]"));

appendString() ähnelt makeString(), hängt jedoch an ein Appendable (wie StringBuilder) und ist void. Es hat die gleichen drei Formen, mit einem zusätzlichen ersten Argument, dem Appendable.

MutableList<Integer> list = FastList.newListWith(1, 2, 3);
Appendable appendable = new StringBuilder();
list.appendString(appendable, "[", "/", "]");
assertEquals("[1/2/3]", appendable.toString());

Wenn Sie Ihre Sammlung nicht in einen Eclipse-Sammlungstyp konvertieren können, passen Sie sie einfach mit dem entsprechenden Adapter an.

List<Object> list = ...;
ListAdapter.adapt(list).makeString(",");

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

2
Craig P. Motlin

Wenn Sie Spring MVC verwenden, können Sie die folgenden Schritte ausführen.

import org.springframework.util.StringUtils;

List<String> groupIds = new List<String>;   
groupIds.add("a");    
groupIds.add("b");    
groupIds.add("c");

String csv = StringUtils.arrayToCommaDelimitedString(groupIds.toArray());

Es wird zu a,b,c

2
Ankit Lalan

Warum nicht eine eigene join () -Methode schreiben? Als Parameter werden eine Auflistung von Zeichenfolgen und eine Trennzeichenfolge verwendet. Durchlaufen Sie innerhalb der Methode die Auflistung und bauen Sie Ihr Ergebnis in einem StringBuffer auf.

2
Dave Costa

Mit Java 5 variablen Argumenten müssen Sie nicht alle Ihre Zeichenfolgen explizit in eine Sammlung oder ein Array einfügen:

import junit.framework.Assert;
import org.junit.Test;

public class StringUtil
{
    public static String join(String delim, String... strings)
    {
        StringBuilder builder = new StringBuilder();

        if (strings != null)
        {
            for (String str : strings)
            {
                if (builder.length() > 0)
                {
                    builder.append(delim).append(" ");
                }
                builder.append(str);
            }
        }           
        return builder.toString();
    }
    @Test
    public void joinTest()
    {
        Assert.assertEquals("", StringUtil.join(",", null));
        Assert.assertEquals("", StringUtil.join(",", ""));
        Assert.assertEquals("", StringUtil.join(",", new String[0]));
        Assert.assertEquals("test", StringUtil.join(",", "test"));
        Assert.assertEquals("foo, bar", StringUtil.join(",", "foo", "bar"));
        Assert.assertEquals("foo, bar, x", StringUtil.join(",", "foo", "bar", "x"));
    }
}
1
Mark B

Java 8 Native Type

List<Integer> example;
example.add(1);
example.add(2);
example.add(3);
...
example.stream().collect(Collectors.joining(","));

Benutzerdefiniertes Java 8-Objekt:

List<Person> person;
...
person.stream().map(Person::getAge).collect(Collectors.joining(","));
1
Luis Aguilar

Für diejenigen, die sich in einem Spring-Kontext befinden, ist auch die Klasse StringUtils nützlich:

Es gibt viele nützliche Verknüpfungen wie:

  • collectionToCommaDelimitedString (Auflistung coll)
  • collectionToDelimitedString (Collection coll, String delim)
  • arrayToDelimitedString (Object [] arr, String delim)

und viele andere.

Dies kann hilfreich sein, wenn Sie Java 8 nicht bereits verwenden und sich bereits in einem Spring-Kontext befinden.

Ich bevorzuge es gegenüber den Apache Commons (obwohl auch sehr gut) für die Sammlungsunterstützung, die einfacher ist:

// Encoding Set<String> to String delimited 
String asString = org.springframework.util.StringUtils.collectionToDelimitedString(codes, ";");

// Decoding String delimited to Set
Set<String> collection = org.springframework.util.StringUtils.commaDelimitedListToSet(asString);
1
рüффп

Sie sollten wahrscheinlich eine StringBuilder mit der append -Methode verwenden, um Ihr Ergebnis zu konstruieren, aber ansonsten ist dies eine ebenso gute Lösung, wie sie Java zu bieten hat).

1
newdayrising

Warum machen Sie nicht in Java) dasselbe, was Sie in Ruby tun, nämlich, dass Sie den durch Trennzeichen getrennten String erst erstellen, nachdem Sie alle Teile zum Array hinzugefügt haben?

ArrayList<String> parms = new ArrayList<String>();
if (someCondition) parms.add("someString");
if (anotherCondition) parms.add("someOtherString");
// ...
String sep = ""; StringBuffer b = new StringBuffer();
for (String p: parms) {
    b.append(sep);
    b.append(p);
    sep = "yourDelimiter";
}

Möglicherweise möchten Sie die for-Schleife in eine separate Hilfsmethode verschieben und statt StringBuffer auch StringBuilder verwenden ...

Bearbeiten: Die Reihenfolge der Anhänge wurde korrigiert.

1
agnul

Also im Grunde so etwas:

public static String appendWithDelimiter(String original, String addition, String delimiter) {

if (original.equals("")) {
    return addition;
} else {
    StringBuilder sb = new StringBuilder(original.length() + addition.length() + delimiter.length());
        sb.append(original);
        sb.append(delimiter);
        sb.append(addition);
        return sb.toString();
    }
}
0
killdash10

Ich weiß nicht, ob das wirklich besser ist, aber es verwendet zumindest StringBuilder, der möglicherweise etwas effizienter ist.

Unten finden Sie einen allgemeineren Ansatz, wenn Sie die Liste der Parameter erstellen können, BEVOR Sie eine Parameterbegrenzung vornehmen.

// Answers real question
public String appendWithDelimiters(String delimiter, String original, String addition) {
    StringBuilder sb = new StringBuilder(original);
    if(sb.length()!=0) {
        sb.append(delimiter).append(addition);
    } else {
        sb.append(addition);
    }
    return sb.toString();
}


// A more generic case.
// ... means a list of indeterminate length of Strings.
public String appendWithDelimitersGeneric(String delimiter, String... strings) {
    StringBuilder sb = new StringBuilder();
    for (String string : strings) {
        if(sb.length()!=0) {
            sb.append(delimiter).append(string);
        } else {
            sb.append(string);
        }
    }

    return sb.toString();
}

public void testAppendWithDelimiters() {
    String string = appendWithDelimitersGeneric(",", "string1", "string2", "string3");
}
0
Mikezx6r

Ihr Ansatz ist nicht schlecht, aber Sie sollten einen StringBuffer anstelle des + -Zeichens verwenden. Das + hat den großen Nachteil, dass für jede einzelne Operation eine neue String-Instanz erstellt wird. Je länger die Saite wird, desto größer ist der Overhead. Die Verwendung eines StringBuffers sollte also der schnellste Weg sein:

public StringBuffer appendWithDelimiter( StringBuffer original, String addition, String delimiter ) {
        if ( original == null ) {
                StringBuffer buffer = new StringBuffer();
                buffer.append(addition);
                return buffer;
        } else {
                buffer.append(delimiter);
                buffer.append(addition);
                return original;
        }
}

Nachdem Sie den String erstellt haben, rufen Sie einfach toString () auf dem zurückgegebenen StringBuffer auf.

0
Yaba
//Note: if you have access to Java5+, 
//use StringBuilder in preference to StringBuffer.  
//All that has to be replaced is the class name.  
//StringBuffer will work in Java 1.4, though.

appendWithDelimiter( StringBuffer buffer, String addition, 
    String delimiter ) {
    if ( buffer.length() == 0) {
        buffer.append(addition);
    } else {
        buffer.append(delimiter);
        buffer.append(addition);
    }
}


StringBuffer parameterBuffer = new StringBuffer();
if ( condition ) { 
    appendWithDelimiter(parameterBuffer, "elementName", "," );
}
if ( anotherCondition ) {
    appendWithDelimiter(parameterBuffer, "anotherElementName", "," );
}

//Finally, to return a string representation, call toString() when returning.
return parameterBuffer.toString(); 
0
MetroidFan2002

die Eingabe von Dollar ist ganz einfach:

String joined = $(aCollection).join(",");

NB: Es funktioniert auch für Array und andere Datentypen

Implementierung

Intern verwendet es einen sehr ordentlichen Trick:

@Override
public String join(String separator) {
    Separator sep = new Separator(separator);
    StringBuilder sb = new StringBuilder();

    for (T item : iterable) {
        sb.append(sep).append(item);
    }

    return sb.toString();
}

die Klasse Separator gibt den leeren String nur beim ersten Aufruf zurück und gibt dann das Trennzeichen zurück:

class Separator {

    private final String separator;
    private boolean wasCalled;

    public Separator(String separator) {
        this.separator = separator;
        this.wasCalled = false;
    }

    @Override
    public String toString() {
        if (!wasCalled) {
            wasCalled = true;
            return "";
        } else {
            return separator;
        }
    }
}
0
dfa

Feste Antwort Rob Dickerson.

Es ist einfacher zu bedienen:

public static String join(String delimiter, String... values)
{
    StringBuilder stringBuilder = new StringBuilder();

    for (String value : values)
    {
        stringBuilder.append(value);
        stringBuilder.append(delimiter);
    }

    String result = stringBuilder.toString();

    return result.isEmpty() ? result : result.substring(0, result.length() - 1);
}
0
maXp

Anstatt die Zeichenfolgenverkettung zu verwenden, sollten Sie StringBuilder verwenden, wenn der Code nicht mit einem Thread versehen ist, und StringBuffer, wenn dies der Fall ist.

0
Aaron

Also ein paar Dinge, die Sie tun könnten, um das Gefühl zu bekommen, dass es so aussieht, als ob Sie suchen:

1) Erweitern Sie die List-Klasse und fügen Sie die Join-Methode hinzu. Die Join-Methode würde einfach das Verketten und Hinzufügen des Trennzeichens übernehmen (was ein Parameter für die Join-Methode sein könnte).

2) Es sieht so aus, als würde Java 7 Java um Erweiterungsmethoden erweitern. Damit können Sie einer Klasse einfach eine bestimmte Methode hinzufügen und diese Join-Methode schreiben und fügen Sie es als Erweiterungsmethode zur Liste oder sogar zur Sammlung hinzu.

Lösung 1 ist wahrscheinlich die einzig realistische Lösung, da Java 7 noch nicht veröffentlicht wurde :) Aber es sollte einwandfrei funktionieren.

Um beide zu verwenden, fügen Sie einfach wie gewohnt alle Ihre Elemente zur Liste oder Sammlung hinzu und rufen dann die neue benutzerdefinierte Methode auf, um sie zu verbinden.

0
user13276

Du machst das ein bisschen komplizierter als es sein muss. Beginnen wir mit dem Ende Ihres Beispiels:

String parameterString = "";
if ( condition ) parameterString = appendWithDelimiter( parameterString, "elementName", "," );
if ( anotherCondition ) parameterString = appendWithDelimiter( parameterString, "anotherElementName", "," );

Mit der kleinen Änderung, einen StringBuilder anstelle eines Strings zu verwenden, wird dies zu:

StringBuilder parameterString = new StringBuilder();
if (condition) parameterString.append("elementName").append(",");
if (anotherCondition) parameterString.append("anotherElementName").append(",");
...

Wenn Sie fertig sind (ich gehe davon aus, dass Sie noch einige andere Bedingungen prüfen müssen), müssen Sie nur das Komma mit einem Befehl wie dem folgenden entfernen:

if (parameterString.length() > 0) 
    parameterString.deleteCharAt(parameterString.length() - 1);

Und schließlich holen Sie sich die Saite, mit der Sie wollen

parameterString.toString();

Sie können auch das "," im zweiten Aufruf durch eine generische Trennzeichenfolge ersetzen, die auf einen beliebigen Wert festgelegt werden kann. Wenn Sie über eine Liste von Dingen verfügen, von denen Sie wissen, dass Sie sie anfügen müssen (ohne Bedingungen), können Sie diesen Code in eine Methode einfügen, die eine Liste von Zeichenfolgen akzeptiert.

0
cjw2600

Leichte Verbesserung [Geschwindigkeit] der Version von izb:

public static String join(String[] strings, char del)
{
    StringBuilder sb = new StringBuilder();
    int len = strings.length;

    if(len > 1) 
    {
       len -= 1;
    }else
    {
       return strings[0];
    }

    for (int i = 0; i < len; i++)
    {
       sb.append(strings[i]).append(del);
    }

    sb.append(strings[i]);

    return sb.toString();
}
0
java al

Ich persönlich verwende ziemlich oft die folgende einfache Lösung für Protokollierungszwecke:

List lst = Arrays.asList("ab", "bc", "cd");
String str = lst.toString().replaceAll("[\\[\\]]", "");
0

Sie können so etwas ausprobieren:

StringBuilder sb = new StringBuilder();
if (condition) { sb.append("elementName").append(","); }
if (anotherCondition) { sb.append("anotherElementName").append(","); }
String parameterString = sb.toString();
0
martinatime