¿La forma más sencilla de delimitar una lista por comas?


104

¿Cuál es la forma más clara de delimitar por comas una lista en Java?

Conozco varias formas de hacerlo, pero me pregunto cuál es la mejor (donde "mejor" significa más clara y / o más corta, no la más eficiente.

Tengo una lista y quiero recorrerla, imprimiendo cada valor. Quiero imprimir una coma entre cada elemento, pero no después del último (ni antes del primero).

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

Solución de muestra 1:

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

Solución de muestra 2: cree una sublista:

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
  }
}

Solución de muestra 3:

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

Estos tratan el primer artículo de manera especial; en su lugar, se podría tratar al último de manera especial.

Por cierto, así es como List toString se implementa (se hereda de AbstractCollection), en 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(", ");
    }
}

Sale del bucle antes de tiempo para evitar la coma después del último elemento. Por cierto: esta es la primera vez que recuerdo haber visto "(esta colección)"; aquí hay un código para provocarlo:

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

Doy la bienvenida a cualquier solución, incluso si usan bibliotecas inesperadas (¿regexp?); y también soluciones en idiomas distintos de Java (por ejemplo Creo Python / Ruby tiene un intersperse función - ¿Cómo se que ? implementada).

Aclaración : por bibliotecas, me refiero a las bibliotecas estándar de Java. Para otras bibliotecas, las considero con otros lenguajes y me interesa saber cómo se implementan.

El kit de herramientas EDIT mencionó una pregunta similar: última iteración del bucle for mejorado en java

Y otro: ¿El último elemento de un bucle merece un tratamiento aparte?


Respuestas:


226

Java 8 y posterior

Usando StringJoinerclase:

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

Usando Stream, y Collectors:

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

Java 7 y versiones anteriores

Véase también # 285523

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

1
¡Una forma muy sorprendente de almacenar el estado! Es casi polimórfico OO. No me gusta la asignación innecesaria en cada ciclo, pero apuesto a que es más eficiente que un if. La mayoría de las soluciones repiten una prueba que sabemos que no será cierta; aunque ineficaces, probablemente sean las más claras. Gracias por el enlace!
13ren

1
Me gusta esto, pero como es inteligente y sorprendente, pensé que no sería apropiado usarlo (¡sorprender no es bueno!). Sin embargo, no pude evitarlo. Todavía no estoy seguro de eso; sin embargo, es tan corto que es fácil de resolver siempre que haya un comentario que lo advierta.
13ren

6
@ 13ren: Tú y el tío Bob necesitan tener una charla sobre el código que sea "inteligente y sorprendente".
Stefan Kendall

Siento que desperdicié años sin saber esto ^^;
Lago

¿Por qué Java necesita convertir esa tarea en un código desagradable?
Eduardo

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

1
Encontró la fuente: svn.apache.org/viewvc/commons/proper/lang/trunk/src/java/org/… El método de unión tiene 9 sobrecargas. Los basados ​​en iteraciones son como "Solución de muestra 3"; los basados ​​en índices usan: if (i> startIndex) {<agregar separador>}
13ren

Sin embargo, estoy realmente detrás de las implementaciones. Es una buena idea resumirlo en una función, pero en la práctica, mi delimitador es a veces una nueva línea y cada línea también tiene una sangría específica. A menos que ... únase (lista, "\ n" + sangría) ... ¿funcionará siempre? Lo siento, solo pienso en voz alta.
13ren

2
En mi opinión, esta es la solución más bonita y corta
Jesper Rønn-Jensen

probablemente, list.iterator ()?
Vanuan

32

Java 8 proporciona varias formas nuevas de hacer esto:

Ejemplo:

// 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();

El enfoque que usa corrientes asume import static java.util.stream.Collectors.joining;.


En mi humilde opinión, si estás usando Java 8, la mejor respuesta.
scravy


7

Si usa Spring Framework, puede hacerlo con StringUtils :

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)

Gracias, pero ¿cómo se implementa?
13ren

6

Basado en la implementación List toString de Java:

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

Utiliza una gramática como esta:

List --> (Item , )* Item

Al estar basado en el último en lugar de en el primero, puede buscar la coma con la misma prueba para verificar el final de la lista. Creo que este es muy elegante, pero no estoy seguro de la claridad.


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

4

Hay una manera bonita de lograr esto usando Java 8:

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

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

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

Sí, es un poco críptico, pero es una alternativa a lo que ya se ha publicado.
Cherouvim

1
Creo que el código se ve bien, en mi humilde opinión, es más bonito que tu otra respuesta y no es 'críptico'. Envuélvalo en un método llamado Join y su risa, aún así, espero que el lenguaje tenga una mejor manera de resolver lo que realmente es un problema común.
Tarn

@tarn: crees que se ve bien porque tienes un fondo de Python / perl;) A mí también me gusta
cherouvim

3

Una opción para el bucle foreach es:

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());
}

Actualización : como mencionó Dave Webb en los comentarios, es posible que esto no produzca resultados correctos si los primeros elementos de la lista son cadenas vacías.


meh .. todavía tiene el if en el bucle.
sfossen

Puede utilizar un bucle foreach para acortarlo.
Peter Lawrey

Eso no funcionará si el primer elemento de la lista es una cadena vacía.
Dave Webb

@Dave Webb: Sí, gracias. Sin embargo, la pregunta no tiene tal requisito.
Cherouvim

@cherovium: La pregunta no dice que ninguno de los elementos será la cadena vacía, por lo que es un requisito implícito. Si está creando un archivo CSV, los campos vacíos pueden ser bastante comunes.
Dave Webb

2

Yo suelo hacer esto:

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()); }

Aunque creo que haré esta verificación de ahora en adelante según la implementación de Java;)


Esa es la misma lógica que la Muestra 3, me gusta porque no hace nada innecesario. Sin embargo, no sé si es el más claro. Por cierto: es un poco más eficiente si pones el while dentro del if, evitando dos pruebas cuando la lista está vacía. También me lo hace un poco más claro.
13ren

Por el amor de Dios, use un StringBuilder
scravy

2

Si puede usar Groovy (que se ejecuta en la JVM):

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

gracias, pero estoy interesado en implementaciones. ¿Cómo se implementa este?
13ren

2

(Copie y pegue mi propia respuesta de aquí ). Muchas de las soluciones descritas aquí son un poco exageradas, en mi humilde opinión, especialmente aquellas que dependen de bibliotecas externas. Hay un lenguaje agradable, limpio y claro para lograr una lista separada por comas que siempre he usado. Se basa en el operador condicional (?):

Editar : Solución original correcta, pero no óptima según los comentarios. Intentando una segunda vez:

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

Ahí lo tienes, en 4 líneas de código, incluida la declaración de la matriz y el StringBuilder.

2da edición : si se trata de un iterador:

    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() ? "," : "");

Esto y stackoverflow.com/questions/668952/… anterior son similares. Es breve y claro, pero si solo tiene un iterador, primero deberá realizar la conversión. Ineficiente, pero ¿quizás valga la pena por la claridad de este enfoque?
13ren

Uf, la concatenación de cadenas crea un StringBuilder temporal con el uso de StringBuilder. Ineficiente. Mejor (más como Java, pero mejor código de ejecución) sería "if (i> 0) {builder.append (',');}" seguido de builder.append (array [i]);
Eddie

Sí, si miras en el bytecode, tienes razón. No puedo creer que una pregunta tan simple pueda volverse tan complicada. Me pregunto si el compilador puede optimizar aquí.
Julien Chastang

Proporcionó la segunda solución, con suerte, mejor.
Julien Chastang

Podría ser mejor dividirlos (para que la gente pueda votar por uno u otro).
13ren

1

Normalmente uso algo similar a la versión 3. Funciona bien c / c ++ / bash / ...: P


1

No lo compilé ... pero debería funcionar (o estar cerca de funcionar).

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());
}

Me gusta de esta manera, solo preferiría otro formato. Pero para mencionar una pequeña solución: si la lista está vacía, esto arrojará una excepción IndexOutOfBounds. Entonces, haga una verificación antes de setLength, o use Math.max (0, ...)
tb-

1

Esto es muy breve, muy claro, pero le da a mi sensibilidad los horrores que se arrastran. También es un poco incómodo adaptarse a diferentes delimitadores, especialmente si un String (no char).

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

Inspirado por: última iteración de bucle for mejorado en java


1
StringBuffer sb = new StringBuffer();

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

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

Encuentro que este es muy fácil de integrar en una clase de utilidad estática, al mismo tiempo que es muy fácil de leer y comprender. Tampoco requiere ninguna variable o estado adicional que no sea la lista en sí, el bucle y el delimitador.
Joachim H. Skeie

Esto proporcionará algo como:Item1Item2, Item3, Item4,

Por el amor de Dios, use un StringBuilder
scravy

1

Me gusta un poco este enfoque, que encontré en un blog hace algún tiempo. Desafortunadamente, no recuerdo el nombre / URL del blog.

Puede crear una clase de utilidad / ayuda con este aspecto:

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;
    }
}

Usar la clase auxiliar es tan simple como esto:

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

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

Solo una nota: debe ser "delimitador", no "delimitador".
Michael Myers

1

Debido a que su delimitador es "," puede utilizar cualquiera de los siguientes:

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;}
    }
}

Si su delimitador es CUALQUIER COSA más, entonces esto no funcionará para usted.


1

Me gusta esta solucion:

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

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

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

Supongo que también puede hacer uso de StringBuilder.


1

En mi opinión, este es el más simple de leer y comprender:

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

0

En Python es fácil

",". únete (tu lista)

En C # hay un método estático en la clase String

String.Join (",", su lista de cadenas)

Lo siento, no estoy seguro acerca de Java, pero pensé en hablar mientras preguntaba por otros idiomas. Estoy seguro de que habría algo similar en Java.


IIRC, obtiene un final, en la versión .Net. :-(

1
Recién probado, sin delimitador final en .NET o Python
tarn

1
¡Gracias! Encontré la fuente: svn.python.org/view/python/branches/release22-branch/Objects/… busque "Catenate everything". Omite el separador del último elemento.
13ren

@ 13ren ¡Bien hecho! Te ayuda? Tenía un candado y recordé rápidamente por qué elegí no programar en C estos días: P
tarn

@tarn, sí, es un ejemplo interesante porque solo agrega el comando si no es el último elemento: if (i <seqlen - 1) {<add separator>}
13ren

0

También puede agregar incondicionalmente la cadena delimitador y, después del bucle, eliminar el delimitador adicional al final. Luego, un "si la lista está vacía, devuelva esta cadena" al principio le permitirá evitar la marca al final (ya que no puede eliminar caracteres de una lista vacía)

Entonces la pregunta realmente es:

"Dado un bucle y un si, ¿cuál crees que es la forma más clara de tenerlos juntos?"


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
Creo que listToString.substring(0, listToString.length()-separator.length());
querías

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]);

Residencia en forma más clara de delimitar una lista por comas (Java)?

Usando esta idea: ¿El último elemento de un bucle merece un tratamiento por separado?


1
Para que esto funcione, debe saber que hay al menos 1 elemento en la lista; de lo contrario, su primer bucle for se bloqueará con una IndexOutOfBoundsException.
Joachim H. Skeie

@Joachim gracias, tienes razón, por supuesto. ¡Qué fea solución escribí! Me cambiaré for (int i=0; i<1; i++)a 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();
}

1
Acabo de notar que es similar a la respuesta que dio
TofuBeer

0

Ninguna de las respuestas usa recursividad hasta ahora ...

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

Método

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();
}

Uso

Dada la matriz de [1,2,3]

join(myArray, ",") // 1,2,3
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.