Der einfachste Weg, eine Liste durch Kommas zu trennen?


104

Was ist der klarste Weg, eine Liste in Java durch Kommas zu trennen?

Ich kenne verschiedene Möglichkeiten, aber ich frage mich, was der beste Weg ist (wobei "am besten" am klarsten und / oder am kürzesten bedeutet, nicht am effizientesten.

Ich habe eine Liste und möchte sie durchlaufen und jeden Wert drucken. Ich möchte zwischen jedem Element ein Komma drucken, aber nicht nach dem letzten (noch vor dem ersten).

List --> Item ( , Item ) *
List --> ( Item , ) * Item

Probenlösung 1:

boolean isFirst = true;
for (Item i : list) {
  if (isFirst) {
    System.out.print(i);        // no comma
    isFirst = false;
  } else {
    System.out.print(", "+i);   // comma
  }
}

Beispiellösung 2 - Erstellen Sie eine Unterliste:

if (list.size()>0) {
  System.out.print(list.get(0));   // no comma
  List theRest = list.subList(1, list.size());
  for (Item i : theRest) {
    System.out.print(", "+i);   // comma
  }
}

Probenlösung 3:

  Iterator<Item> i = list.iterator();
  if (i.hasNext()) {
    System.out.print(i.next());
    while (i.hasNext())
      System.out.print(", "+i.next());
  }

Diese behandeln den ersten Gegenstand besonders; man könnte stattdessen den letzten speziell behandeln.

Im übrigen ist hier, wie List toString wird umgesetzt (es ist geerbt von AbstractCollection), in Java 1.6:

public String toString() {
    Iterator<E> i = iterator();
    if (! i.hasNext())
        return "[]";

    StringBuilder sb = new StringBuilder();
    sb.append('[');
    for (;;) {
        E e = i.next();
        sb.append(e == this ? "(this Collection)" : e);
        if (! i.hasNext())
            return sb.append(']').toString();
        sb.append(", ");
    }
}

Es verlässt die Schleife vorzeitig, um das Komma nach dem letzten Element zu vermeiden. Übrigens: Dies ist das erste Mal, dass ich mich erinnere, "(diese Sammlung)" gesehen zu haben. Hier ist der Code, um es zu provozieren:

List l = new LinkedList();
l.add(l);
System.out.println(l);

Ich begrüße jede Lösung, auch wenn sie unerwartete Bibliotheken verwenden (regulärer Ausdruck?); und auch Lösungen in anderen Sprachen als Java (z. B. denke ich, dass Python / Ruby eine Intersperse- Funktion haben - wie ist das implementiert?).

Klarstellung : Mit Bibliotheken meine ich die Standard-Java-Bibliotheken. Für andere Bibliotheken betrachte ich sie mit anderen Sprachen und bin daran interessiert zu wissen, wie sie implementiert sind.

Das EDIT- Toolkit erwähnte eine ähnliche Frage: Letzte Iteration der erweiterten for-Schleife in Java

Und noch eines: Verdient das letzte Element in einer Schleife eine separate Behandlung?


Antworten:


226

Java 8 und höher

Mit StringJoinerKlasse:

StringJoiner joiner = new StringJoiner(",");
for (Item item : list) {
    joiner.add(item.toString());
}
return joiner.toString();

Verwenden von Streamund Collectors:

return list.stream().
       map(Object::toString).
       collect(Collectors.joining(",")).toString();

Java 7 und früher

Siehe auch # 285523

String delim = "";
for (Item i : list) {
    sb.append(delim).append(i);
    delim = ",";
}

1
Eine sehr überraschende Art, den Zustand zu speichern! Es ist fast OO polymorph. Ich mag die unnötige Zuweisung nicht für jede Schleife, aber ich wette, sie ist effizienter als ein Wenn. Die meisten Lösungen wiederholen einen Test, von dem wir wissen, dass er nicht wahr ist - obwohl er ineffizient ist, sind sie wahrscheinlich der klarste. Danke für den Link!
13ren

1
Ich mag das, aber weil es klug und überraschend ist, dachte ich, es wäre nicht angebracht, es zu verwenden (überraschend ist nicht gut!). Ich konnte mir jedoch nicht helfen. Ich bin mir immer noch nicht sicher; Es ist jedoch so kurz, dass es leicht zu trainieren ist, vorausgesetzt, es gibt einen Kommentar, der Sie darauf hinweist.
13ren

6
@ 13ren: Sie und Onkel Bob müssen über Code sprechen, der "klug und überraschend" ist.
Stefan Kendall

Ich habe das Gefühl, ich habe Jahre damit verschwendet, das nicht zu wissen ^^;
See

Warum muss Java eine solche Aufgabe in einen hässlichen Code verwandeln?
Eduardo

82
org.apache.commons.lang3.StringUtils.join(list,",");

1
Die Quelle wurde gefunden: svn.apache.org/viewvc/commons/proper/lang/trunk/src/java/org/… Die Join-Methode weist 9 Überladungen auf. Die iterationsbasierten sind wie "Beispiellösung 3"; Die indexbasierten verwenden: if (i> startIndex) {<Trennzeichen hinzufügen>}
13ren

Ich bin wirklich nach Implementierungen. Es ist eine gute Idee, es in eine Funktion zu packen, aber in der Praxis ist mein Trennzeichen manchmal eine neue Zeile, und jede Zeile wird auch bis zu einer bestimmten Tiefe eingerückt. Es sei denn ... beitreten (Liste, "\ n" + Einzug) ... wird das immer funktionieren? Entschuldigung, ich denke nur laut nach.
13ren

2
Meiner Meinung nach ist dies die schönste und kürzeste Lösung
Jesper Rønn-Jensen

wahrscheinlich list.iterator ()?
Vanuan

32

Java 8 bietet dazu mehrere neue Möglichkeiten:

Beispiel:

// Util method for strings and other char sequences
List<String> strs = Arrays.asList("1", "2", "3");
String listStr1 = String.join(",", strs);

// For any type using streams and collectors
List<Object> objs = Arrays.asList(1, 2, 3);
String listStr2 = objs.stream()
    .map(Object::toString)
    .collect(joining(",", "[", "]"));

// Using the new StringJoiner class
StringJoiner joiner = new StringJoiner(", ", "[", "]");
joiner.setEmptyValue("<empty>");
for (Integer i : objs) {
  joiner.add(i.toString());
}
String listStr3 = joiner.toString();

Der Ansatz mit Streams setzt voraus import static java.util.stream.Collectors.joining;.


Dies ist meiner Meinung nach die beste Antwort, wenn Sie Java 8 verwenden.
Scravy


7

Wenn Sie das Spring Framework verwenden, können Sie dies mit StringUtils tun :

public static String arrayToDelimitedString(Object[] arr)
public static String arrayToDelimitedString(Object[] arr, String delim)
public static String collectionToCommaDelimitedString(Collection coll)
public static String collectionToCommaDelimitedString(Collection coll, String delim)

Danke, aber wie ist es implementiert?
13ren

6

Basierend auf der Implementierung von Java List toString:

Iterator i = list.iterator();
for (;;) {
  sb.append(i.next());
  if (! i.hasNext()) break;
  ab.append(", ");
}

Es verwendet eine Grammatik wie diese:

List --> (Item , )* Item

Wenn es auf der letzten statt auf der ersten Basis basiert, kann es mit demselben Test nach Überspringkomma suchen, um nach dem Ende der Liste zu suchen. Ich finde diesen sehr elegant, bin mir aber bei der Klarheit nicht sicher.


4
String delimiter = ",";
StringBuilder sb = new StringBuilder();
for (Item i : list) {
    sb.append(delimiter).append(i);
}
sb.toString().replaceFirst(delimiter, "");

4

Es gibt eine schöne Möglichkeit, dies mit Java 8 zu erreichen:

List<String> list = Arrays.asList(array);
String joinedString = String.join(",", list);

TextUtils.join (",", Liste);
Arul Pandian

3
for(int i=0, length=list.size(); i<length; i++)
  result+=(i==0?"":", ") + list.get(i);

Ja, es ist ein bisschen kryptisch, aber eine Alternative zu dem, was bereits gepostet wurde.
Cherouvim

1
Ich denke, der Code sieht gut aus, meiner Meinung nach ist er hübscher als Ihre andere Antwort und kaum "kryptisch". Wickeln Sie es in eine Methode namens Join und Ihr Lachen ein, dennoch würde ich erwarten, dass die Sprache eine bessere Möglichkeit hat, das wirklich häufige Problem zu lösen.
Tarn

@ Tarn: Du denkst, es sieht gut aus, weil du einen Python / Perl-Hintergrund hast;) Ich mag es auch
Cherouvim

3

Eine Option für die foreach-Schleife ist:

StringBuilder sb = new StringBuilder();
for(String s:list){
  if (sb.length()>0) sb.append(",");
  sb.append(s);
}

2
StringBuffer result = new StringBuffer();
for(Iterator it=list.iterator; it.hasNext(); ) {
  if (result.length()>0)
    result.append(", ");
  result.append(it.next());
}

Update : Wie Dave Webb in den Kommentaren erwähnt hat, führt dies möglicherweise nicht zu korrekten Ergebnissen, wenn die ersten Elemente in der Liste leere Zeichenfolgen sind.


meh .. hat noch das wenn in der schleife.
sfossen

Sie können eine foreach-Schleife verwenden, um sie zu verkürzen.
Peter Lawrey

Das funktioniert nicht, wenn das erste Element in der Liste eine leere Zeichenfolge ist.
Dave Webb

@ Dave Webb: Ja, danke. Die Frage hat jedoch keine solche Anforderung.
Cherouvim

@cherovium: Die Frage besagt nicht, dass keines der Elemente die leere Zeichenfolge ist, daher ist dies eine implizite Anforderung. Wenn Sie eine CSV-Datei erstellen, können leere Felder häufig vorkommen.
Dave Webb

2

Normalerweise mache ich das:

StringBuffer sb = new StringBuffer();
Iterator it = myList.iterator();
if (it.hasNext()) { sb.append(it.next().toString()); }
while (it.hasNext()) { sb.append(",").append(it.next().toString()); }

Obwohl ich denke, dass ich diese Überprüfung von nun an gemäß der Java-Implementierung durchführen werde;)


Das ist die gleiche Logik wie in Beispiel 3, ich mag es, weil es nichts Unnötiges tut. Ich weiß jedoch nicht, ob es am klarsten ist. Übrigens: Es ist etwas effizienter, wenn Sie das while in das if einfügen, indem Sie zwei Tests vermeiden, wenn die Liste leer ist. Es macht es mir auch etwas klarer.
13ren

Um Himmels willen, verwenden Sie einen StringBuilder
Scravy

2

Wenn Sie Groovy verwenden können (das auf der JVM ausgeführt wird):

def list = ['a', 'b', 'c', 'd']
println list.join(',')

Danke, aber ich interessiere mich für Implementierungen. Wie ist dieser implementiert?
13ren

2

(Kopieren und Einfügen meiner eigenen Antwort von hier .) Viele der hier beschriebenen Lösungen sind meiner Meinung nach etwas übertrieben, insbesondere diejenigen, die auf externen Bibliotheken basieren. Es gibt eine schöne, saubere und klare Sprache, um eine durch Kommas getrennte Liste zu erhalten, die ich immer verwendet habe. Es basiert auf dem bedingten (?) Operator:

Bearbeiten : Ursprüngliche Lösung korrekt, aber laut Kommentaren nicht optimal. Ein zweites Mal versuchen:

int[] array = {1, 2, 3};
StringBuilder builder = new StringBuilder();
for (int i = 0 ;  i < array.length; i++)
       builder.append(i == 0 ? "" : ",").append(array[i]);

Los geht's in 4 Codezeilen, einschließlich der Deklaration des Arrays und des StringBuilder.

2. Bearbeitung : Wenn Sie es mit einem Iterator zu tun haben:

    List<Integer> list = Arrays.asList(1, 2, 3);
    StringBuilder builder = new StringBuilder();
    for (Iterator it = list.iterator(); it.hasNext();)
        builder.append(it.next()).append(it.hasNext() ? "," : "");

Dies und stackoverflow.com/questions/668952/… oben sind ähnlich. Es ist kurz und klar, aber wenn Sie nur einen Iterator haben, müssen Sie zuerst konvertieren. Ineffizient, aber vielleicht wert für die Klarheit dieses Ansatzes?
13ren

Ugh, String-Verkettung, die einen temporären StringBuilder unter Verwendung eines StringBuilder erstellt. Ineffizient. Besser (mehr Java, aber besser laufender Code) wäre "if (i> 0) {builder.append (',');}", gefolgt von builder.append (array [i]);
Eddie

Ja, wenn Sie in den Bytecode schauen, haben Sie Recht. Ich kann nicht glauben, dass eine so einfache Frage so kompliziert werden kann. Ich frage mich, ob der Compiler hier optimieren kann.
Julien Chastang

Vorausgesetzt, 2., hoffentlich bessere Lösung.
Julien Chastang

Könnte besser sein, sie zu teilen (damit die Leute über das eine oder andere abstimmen können).
13ren

1

Normalerweise benutze ich etwas ähnliches wie Version 3. Es funktioniert gut c / c ++ / bash / ...: P.


1

Ich habe es nicht kompiliert ... sollte aber funktionieren (oder kurz vor der Arbeit stehen).

public static <T> String toString(final List<T> list, 
                                  final String delim)
{
    final StringBuilder builder;

    builder = new StringBuilder();

    for(final T item : list)
    {
        builder.append(item);
        builder.append(delim);
    }

    // kill the last delim
    builder.setLength(builder.length() - delim.length());

    return (builder.toString());
}

Ich mag diese Art, ich würde nur eine andere Formatierung bevorzugen. Um nur eine kleine Korrektur zu erwähnen: Wenn die Liste leer ist, wird eine IndexOutOfBounds-Ausnahme ausgelöst. Führen Sie also entweder eine Überprüfung vor der setLength durch oder verwenden Sie Math.max (0, ...)
tb

1

Das ist sehr kurz, sehr klar, aber es gibt meiner Sensibilität die schleichenden Schrecken. Es ist auch etwas umständlich, sich an verschiedene Trennzeichen anzupassen, insbesondere wenn es sich um einen String (nicht char) handelt.

for (Item i : list)
  sb.append(',').append(i);
if (sb.charAt(0)==',') sb.deleteCharAt(0);

Inspiriert von: Letzte Iteration der erweiterten for-Schleife in Java


1
StringBuffer sb = new StringBuffer();

for (int i = 0; i < myList.size(); i++)
{ 
    if (i > 0) 
    {
        sb.append(", ");
    }

    sb.append(myList.get(i)); 
}

Ich finde es sehr einfach, diese in eine statische Utility-Klasse einzubetten, während sie auch sehr einfach zu lesen und zu verstehen ist. Es sind auch keine zusätzlichen Variablen oder Zustände außer der Liste selbst, der Schleife und dem Trennzeichen erforderlich.
Joachim H. Skeie

Dies wird etwas bieten wie:Item1Item2, Item3, Item4,

Um Himmels willen, verwenden Sie einen StringBuilder
Scravy

1

Ich mag diesen Ansatz, den ich vor einiger Zeit in einem Blog gefunden habe. Leider erinnere ich mich nicht an den Namen / die URL des Blogs.

Sie können eine Dienstprogramm- / Hilfsklasse erstellen, die folgendermaßen aussieht:

private class Delimiter
{
    private final String delimiter;
    private boolean first = true;

    public Delimiter(String delimiter)
    {
        this.delimiter = delimiter;
    }

    @Override
    public String toString()
    {
        if (first) {
            first = false;
            return "";
        }

        return delimiter;
    }
}

Die Verwendung der Hilfsklasse ist so einfach:

StringBuilder sb = new StringBuilder();
Delimiter delimiter = new Delimiter(", ");

for (String item : list) {
    sb.append(delimiter);
    sb.append(item);
}

Nur eine Anmerkung: Es sollte "Begrenzer" sein, nicht "Begrenzer".
Michael Myers

1

Da Ihr Trennzeichen "" ist, können Sie Folgendes verwenden:

public class StringDelim {

    public static void removeBrackets(String string) {
        System.out.println(string.substring(1, string.length() - 1));
    }

    public static void main(String... args) {
        // Array.toString() example
        String [] arr = {"Hi" , "My", "name", "is", "br3nt"};
        String string = Arrays.toString(arr);
        removeBrackets(string);

        // List#toString() example
        List<String> list = new ArrayList<String>();
        list.add("Hi");
        list.add("My");
        list.add("name");
        list.add("is");
        list.add("br3nt");
        string = list.toString();
        removeBrackets(string);

        // Map#values().toString() example
        Map<String, String> map = new LinkedHashMap<String, String>();
        map.put("1", "Hi");
        map.put("2", "My");
        map.put("3", "name");
        map.put("4", "is");
        map.put("5", "br3nt");
        System.out.println(map.values().toString());
        removeBrackets(string);

        // Enum#toString() example
        EnumSet<Days> set = EnumSet.allOf(Days.class);
        string = set.toString();
        removeBrackets(string);
    }

    public enum Days {
        MON("Monday"),
        TUE("Tuesday"),
        WED("Wednesday"),
        THU("Thursday"),
        FRI("Friday"),
        SAT("Saturday"),
        SUN("Sunday");

        private final String day;

        Days(String day) {this.day = day;}
        public String toString() {return this.day;}
    }
}

Wenn Ihr Trennzeichen etwas anderes ist, funktioniert dies für Sie nicht.


1

Ich mag diese Lösung:

String delim = " - ", string = "";

for (String item : myCollection)
    string += delim + item;

string = string.substring(delim.length());

Ich gehe davon aus, dass es auch StringBuilder verwenden kann.


1

Meiner Meinung nach ist dies am einfachsten zu lesen und zu verstehen:

StringBuilder sb = new StringBuilder();
for(String string : strings) {
    sb.append(string).append(',');
}
sb.setLength(sb.length() - 1);
String result = sb.toString();

0

In Python ist es einfach

",". beitreten (Ihre Liste)

In C # gibt es eine statische Methode für die String-Klasse

String.Join (",", yourlistofstrings)

Entschuldigung, ich bin mir bei Java nicht sicher, dachte aber, ich würde mich melden, als Sie nach anderen Sprachen fragten. Ich bin mir sicher, dass es in Java etwas Ähnliches geben würde.


IIRC erhalten Sie ein Trailing in der .Net-Version. :-(

1
Gerade getestet, kein abschließendes Trennzeichen in .NET oder Python
Tarn

1
Vielen Dank! Ich habe die Quelle dafür gefunden: svn.python.org/view/python/branches/release22-branch/Objects/… Suche nach "Alles verketten ". Das Trennzeichen für das letzte Element wird weggelassen.
13ren

@ 13ren Schön gemacht! Hilft es? Ich hatte eine Sperre und erinnerte mich schnell daran, warum ich mich heutzutage entschieden habe, nicht in C zu programmieren: P
tarn

@tarn, ja, es ist ein interessantes Beispiel, weil es nur dann das Komman hinzufügt, wenn es nicht das letzte Element ist: if (i <seqlen - 1) {<Trennzeichen hinzufügen>}
13ren

0

Sie können die Trennzeichenfolge auch bedingungslos hinzufügen und nach der Schleife das zusätzliche Trennzeichen am Ende entfernen. Wenn Sie am Anfang eine "Wenn die Liste leer ist, geben Sie diese Zeichenfolge zurück" verwenden, können Sie die Prüfung am Ende vermeiden (da Sie keine Zeichen aus einer leeren Liste entfernen können).

Die Frage ist also wirklich:

"Was ist Ihrer Meinung nach angesichts einer Schleife und eines Wenn der klarste Weg, diese zusammen zu haben?"


0
public static String join (List<String> list, String separator) {
  String listToString = "";

  if (list == null || list.isEmpty()) {
   return listToString;
  }

  for (String element : list) {
   listToString += element + separator;
  }

  listToString = listToString.substring(0, separator.length());

  return listToString;
}

1
Ich denke, Sie listToString.substring(0, listToString.length()-separator.length());
wollten

0
if (array.length>0)          // edited in response Joachim's comment
  sb.append(array[i]);
for (int i=1; i<array.length; i++)
  sb.append(",").append(array[i]);

Basierend auf dem klarsten Weg, eine Liste durch Kommas zu trennen (Java)?

Mit dieser Idee: Verdient das letzte Element in einer Schleife eine separate Behandlung?


1
Damit dies funktioniert, müssen Sie wissen, dass mindestens 1 Element in der Liste enthalten ist. Andernfalls stürzt Ihre erste for-Schleife mit einer IndexOutOfBoundsException ab.
Joachim H. Skeie

@ Joachim danke, du hast natürlich recht. Was für eine hässliche Lösung, die ich geschrieben habe! Ich werde ändern for (int i=0; i<1; i++)zu if (array.length>0).
13ren

0
public String toString(List<Item> items)
{
    StringBuilder sb = new StringBuilder("[");

    for (Item item : items)
    {
        sb.append(item).append(", ");
    }

    if (sb.length() >= 2)
    {
        //looks cleaner in C# sb.Length -= 2;
        sb.setLength(sb.length() - 2);
    }

    sb.append("]");

    return sb.toString();
}


0

Keine der Antworten verwendet bisher Rekursion ...

public class Main {

    public static String toString(List<String> list, char d) {
        int n = list.size();
        if(n==0) return "";
        return n > 1 ? Main.toString(list.subList(0, n - 1), d) + d
                  + list.get(n - 1) : list.get(0);
    }

    public static void main(String[] args) {
        List<String> list = Arrays.asList(new String[]{"1","2","3"});
        System.out.println(Main.toString(list, ','));
    }

}

0
private String betweenComma(ArrayList<String> strings) {
    String united = "";
    for (String string : strings) {
        united = united + "," + string;
    }
    return united.replaceFirst(",", "");
}

-1

Methode

String join(List<Object> collection, String delimiter){
    StringBuilder stringBuilder = new StringBuilder();
    int size = collection.size();
    for (Object value : collection) {
        size --;
        if(size > 0){
            stringBuilder.append(value).append(delimiter);
        }
    }

    return stringBuilder.toString();
}

Verwendung

Angesichts der Anordnung von [1,2,3]

join(myArray, ",") // 1,2,3
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.