La mejor manera de convertir una ArrayList en una cadena

353

Tengo un ArrayListque quiero generar completamente como una cadena. Esencialmente quiero generarlo en orden usando el toStringde cada elemento separado por pestañas. ¿Hay alguna forma rápida de hacer esto? Puede recorrerlo (o eliminar cada elemento) y concatenarlo en una Cadena, pero creo que esto será muy lento.

Juan Besa
fuente
44
o elimine cada elemento Tenga cuidado, eliminar elementos de una lista ArrayList es un gran NO-NO, ya que su "bucle" tomará un tiempo cuadrático debido a los elementos cambiantes (siempre que realice un bucle del primer al último elemento).
Dimitry K
Lo mismo para las colecciones: stackoverflow.com/questions/395401/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

329

Básicamente, usar un bucle para iterar sobre ArrayListes la única opción:

NO use este código, continúe leyendo hasta el final de esta respuesta para ver por qué no es deseable y qué código debe usarse en su lugar:

ArrayList<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");

String listString = "";

for (String s : list)
{
    listString += s + "\t";
}

System.out.println(listString);

De hecho, una concatenación de cadenas estará bien, ya que el javaccompilador optimizará la concatenación de cadenas como una serie de appendoperaciones de StringBuildertodos modos. Aquí hay una parte del desmontaje del bytecode del forbucle del programa anterior:

   61:  new #13; //class java/lang/StringBuilder
   64:  dup
   65:  invokespecial   #14; //Method java/lang/StringBuilder."<init>":()V
   68:  aload_2
   69:  invokevirtual   #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   72:  aload   4
   74:  invokevirtual   #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   77:  ldc #16; //String \t
   79:  invokevirtual   #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   82:  invokevirtual   #17; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

Como se puede ver, el compilador optimiza ese ciclo mediante el uso de a StringBuilder, por lo que el rendimiento no debería ser una gran preocupación.

(OK, en una segunda mirada, StringBuilderse está instanciando el en cada iteración del bucle, por lo que puede que no sea el código de bytes más eficiente. La creación de instancias y el uso de un explícito StringBuilderprobablemente produciría un mejor rendimiento).

De hecho, creo que tener cualquier tipo de salida (ya sea en el disco o en la pantalla) será al menos un orden de magnitud más lento que tener que preocuparse por el rendimiento de las concatenaciones de cadenas.

Editar: Como se señaló en los comentarios, la optimización del compilador anterior está creando una nueva instancia de StringBuildercada iteración. (Lo cual he notado anteriormente).

La técnica más optimizada para usar será la respuesta de Paul Tomblin , ya que solo crea una instancia de un StringBuilderobjeto fuera del forbucle.

Reescribiendo el código anterior para:

ArrayList<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");

StringBuilder sb = new StringBuilder();
for (String s : list)
{
    sb.append(s);
    sb.append("\t");
}

System.out.println(sb.toString());

Solo creará StringBuilderuna instancia fuera del bucle y solo realizará las dos llamadas al appendmétodo dentro del bucle, como se evidencia en este código de bytes (que muestra la instanciación StringBuildery el bucle):

   // Instantiation of the StringBuilder outside loop:
   33:  new #8; //class java/lang/StringBuilder
   36:  dup
   37:  invokespecial   #9; //Method java/lang/StringBuilder."<init>":()V
   40:  astore_2

   // [snip a few lines for initializing the loop]
   // Loading the StringBuilder inside the loop, then append:
   66:  aload_2
   67:  aload   4
   69:  invokevirtual   #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   72:  pop
   73:  aload_2
   74:  ldc #15; //String \t
   76:  invokevirtual   #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   79:  pop

Entonces, de hecho, la optimización de la mano debería tener un mejor rendimiento, ya que el interior del forbucle es más corto y no hay necesidad de crear una instancia StringBuilderen cada iteración.

Coobird
fuente
8
Considere usar una clase StringBuilder en lugar de solo agregar cadenas, por razones de rendimiento.
Jeremy
55
El compilador optimizará la concatenación de cadenas pero creará un nuevo objeto StringBuilder cada vez que se ejecute el bucle.
Pedro Henriques
44
-1 para el uso de + =, en este caso es un asesino de rendimiento. Siempre use StringBuilder para agregar repetidamente a una cadena.
starblue 01 de
10
Esta solución agrega un "\ t" adicional al final de la cadena que a menudo no es deseable, especialmente cuando desea crear una lista separada por comas o similar. Creo que las otras soluciones publicadas aquí, usando Apache Commons o Guava, son superiores.
Peter Goetz
3
Esta no es la mejor respuesta. Mire abajo en Vitalii para Javao Geewax paraAndroid
Gibolt
895

En Java 8 o posterior:

String listString = String.join(", ", list);

En caso de listque no sea de tipo String, se puede usar un recopilador de unión:

String listString = list.stream().map(Object::toString)
                        .collect(Collectors.joining(", "));
Vitalii Fedorenko
fuente
144
No puedo creer que haya tardado hasta Java 8 en agregar esto.
Paul
45
¡Esta respuesta debería ser votada más! Sin hacks, sin bibliotecas y sin bucles.
Songo
44
esto no funciona para lo que dijo el OP. String.join toma como segundo argumento Iterable <? extiende CharSequence>. Por lo tanto, funciona si tiene una Lista <String> que desea mostrar, pero si tiene Lista <MiObjeto> no llamará a toString () como lo desea el OP
Hilikus
3
¿Por qué demonios esta no es la primera respuesta (y aceptada)? Siempre tengo que desplazarme hacia abajo hasta que sé que está allí ... elegante y conciso
Jack
2
Requiere Android API 26+
Arsen Sench
391

Si está haciendo esto en Android, hay una buena utilidad para esto llamada TextUtils que tiene un .join(String delimiter, Iterable)método.

List<String> list = new ArrayList<String>();
list.add("Item 1");
list.add("Item 2");
String joined = TextUtils.join(", ", list);

Obviamente no uso mucho fuera de Android, pero pensé que lo agregaría a este hilo ...

JJ Geewax
fuente
1
Como TextUtils.jointoma un Object[], supongo que está llamando toString()a cada token. ¿ PatientDetailsImplementa toString()? Si eso no resuelve el problema, puede estar atrapado haciendo algo con la Separatorclase.
JJ Geewax
55
org.apache.lang3.StringUtilshace prácticamente lo mismo, por lo que su respuesta me fue de mucha utilidad fuera de Android :)
Patrick
1
La popularidad de esta respuesta (actualmente la más votada) muestra que muchas preguntas de Java surgen del desarrollo de Android
Amr H. Abd Elmajeed
1
Puedo apostar que fueron enviados a la Tierra para resolver mi problema :)
kofoworola
1
Me salvaste la vida. <3
Pratik Butani
234

Descargue Apache Commons Lang y use el método

 StringUtils.join(list)

 StringUtils.join(list, ", ") // 2nd param is the separator.

Puede implementarlo usted mismo, por supuesto, pero su código está completamente probado y es probablemente la mejor implementación posible.

Soy un gran admirador de la biblioteca Apache Commons y también creo que es una gran adición a la Biblioteca estándar de Java.

Ravi Wallau
fuente
63
Desafortunadamente, los habitantes de SO prefieren reinventar la rueda.
skaffman
33
También en Apache Commons Lang. El uso se vería asíStringUtils.join(list.toArray(),"\t")
Muhd
33
Esta rueda en particular apenas vale una dependencia extra.
Seva Alekseyev
25
@SevaAlekseyev Creo que es discutible. Si agrega esta dependencia de inmediato, utilizará sus clases con mucha frecuencia. En lugar de usar "if string! = Null && string.trim ()! =" "Usará StringUtils.isNotEmpty ... Utilizará ObjectUtils.equals () para que no tenga que buscar nulos en todas partes. Pero si espera a que la dependencia que justifica el uso de estas bibliotecas, es posible que en realidad nunca usarlos.
Ravi Wallau
66
¡También es bueno que no agregue una pestaña adicional al final!
keuleJ
139

Esta es una pregunta bastante antigua, pero creo que también podría agregar una respuesta más moderna: use la Joinerclase de Guava :

String joined = Joiner.on("\t").join(list);
Jon Skeet
fuente
2
Esta es mi solución favorita. : -> +1 para guayaba.
csgeek
Ctrl + F'd para guayaba también. ¡Gracias!
Priidu Neemre
List <String> list = new ArrayList <String> (); list.add ("Hola"); list.add ("Hai"); list.add ("Hola"); System.out.println (list.toString (). Substring (1, list.toString (). Length () - 1) .replaceAll (",", "\ t"));
Lova Chittumuri
86

Cambiar la Lista a una Cadena legible y significativa es realmente una pregunta común que todos pueden encontrar.

Caso 1 . Si tiene StringUtils de apache en su ruta de clase (como de rogerdpack y Ravi Wallau):

import org.apache.commons.lang3.StringUtils;
String str = StringUtils.join(myList);

Caso 2 . Si solo desea utilizar formas de JDK (7):

import java.util.Arrays;
String str = Arrays.toString(myList.toArray()); 

Simplemente nunca construya ruedas usted mismo, no use el bucle para esta tarea de una línea.

Sheng.W
fuente
1
No noté su solución de Caso 2 antes de publicar la mía. El tuyo es mucho mejor y ahorra el paso adicional. Definitivamente lo recomendaría sobre algunas de las respuestas que parecen requerir herramientas adicionales.
Elliander
55
Arrays.toString(myList.toArray())- la solución más fácil y sencilla
Ondrej Bozek
Creo que esto debería considerarse como la mejor y más eficiente solución
Morey
Arrays.toString(list.toArray())Es fácil de escribir, pero altamente ineficiente.
Matthieu
60

Si estaba buscando una línea rápida, a partir de Java 5 puede hacer esto:

myList.toString().replaceAll("\\[|\\]", "").replaceAll(", ","\t")

Además, si su propósito es imprimir el contenido y está menos preocupado por el "\ t", simplemente puede hacer esto:

myList.toString()

que devuelve una cadena como

[str1, str2, str3]

Si tiene una matriz (no ArrayList), puede lograr lo mismo de esta manera:

 Arrays.toString(myList).replaceAll("\\[|\\]", "").replaceAll(", ","\t")
Ken Shih
fuente
3
Creo que esto es realmente más eficiente que los métodos publicados anteriormente. Es una pena que se muestre en la parte inferior. Puede que no sea elegante, pero creo firmemente que su solución ejecuta O (n) mientras que los otros teóricamente se ejecutan en O (n ^ 2) (dado que las cadenas de Java son inmutables, básicamente crea una nueva cadena en cada fusión, y empeora a medida que Cadena resultante se hace más grande)
user3790827
Si tu texto es muy grande, terminas consumiendo los recursos de tu computadora.
user3790827
Este método es 7-8 veces más lento que usar StringBuilder.
Zoka
@ user3790827 Estás viendo un nivel muy superficial. Si te digo que busques a una persona en mhogares en nciudades de los xestados, dirías que es O(mnx)o O(n^3), pero puedo reformularlo para decir "busca a esta persona en este país", y me lo dirías de inmediato O(n). Además de eso, todos los algoritmos aquí son generalmente O(n), no hay bucle dentro de un bucle.
Jai
39

Recorrerlo y llamar a String. No hay una forma mágica, y si la hubiera, ¿qué crees que estaría haciendo bajo las sábanas además de recorrerla? Casi la única microoptimización sería usar StringBuilder en lugar de String, e incluso eso no es una gran victoria: la concatenación de cadenas se convierte en StringBuilder debajo de las cubiertas, pero al menos si lo escribe de esa manera, puede ver lo que está sucediendo.

StringBuilder out = new StringBuilder();
for (Object o : list)
{
  out.append(o.toString());
  out.append("\t");
}
return out.toString();
Paul Tomblin
fuente
¡Gracias! esto es lo que termino haciendo :)
Juan Besa
44
Para grandes matrices, esto definitivamente no es micro-optimización. La conversión automática para usar StringBuilder se realiza por separado para cada concatenación de cadenas, lo que no ayuda en el caso de un bucle. Está bien si concatena una gran cantidad de elementos en una expresión.
starblue 01 de
1
Usar el StringBuilder explícitamente es un gran problema si está concatenando, digamos 50 000 cadenas que producen una cadena de resultados de ~ 1 MB; puede ser una diferencia de 1 minuto en comparación con el tiempo de ejecución de 1 s.
Sr. Napik
36

La mayoría de los proyectos Java a menudo tienen apache-commons lang disponibles. Los métodos StringUtils.join () son muy agradables y tienen varios sabores para satisfacer casi todas las necesidades.

public static java.lang.String join(java.util.Collection collection,
                                    char separator)


public static String join(Iterator iterator, String separator) {
    // handle null, zero and one elements before building a buffer 
    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();
}

Parámetros:

colección : la colección de valores para unir, puede ser nula

separador - el carácter separador a usar

Devuelve : la cadena unida, nula si la entrada de iterador es nula

Desde: 2.3

Brian
fuente
2
Esto es muy bueno ya que el join(...)método tiene variantes para matrices y colecciones.
vikingsteve
Necesitaba crear colecciones de cadenas con elementos raramente poblados, con números aleatorios. Al final, necesitaba una cadena, no una matriz, no una matriz a una cadena. Este es mi código: Collections.shuffle (L); StringBuilder sb = new StringBuilder (); L.forEach (e -> sb.append (e)); return sb.toString ();
dobrivoje
14

Para este caso de uso simple, simplemente puede unir las cadenas con una coma. Si usa Java 8:

String csv = String.join("\t", yourArray);

de lo contrario, commons-lang tiene un método join ():

String csv = org.apache.commons.lang3.StringUtils.join(yourArray, "\t");
Khader MA
fuente
13

Una forma elegante de lidiar con los caracteres de separación final es usar Class Separator

StringBuilder buf = new StringBuilder();
Separator sep = new Separator("\t");
for (String each: list) buf.append(sep).append(each);
String s = buf.toString();

El método toString de Class Separator devuelve el separador, a excepción de la primera llamada. Por lo tanto, imprimimos la lista sin separadores iniciales (o en este caso) finales.

akuhn
fuente
8

En Java 8 es simple. Vea el ejemplo para la lista de enteros:

String result = Arrays.asList(1,2,3).stream().map(Object::toString).reduce((t, u) -> t + "\t" + u).orElse("");

O versión multilínea (que es más fácil de leer):

String result = Arrays.asList(1,2,3).stream()
    .map(Object::toString)
    .reduce((t, u) -> t + "\t" + u)
    .orElse("");

Actualización: una versión más corta

String result = Arrays.asList(1,2,3).stream()
                .map(Object::toString)
                .collect(Collectors.joining("\t"));
amra
fuente
2
Esto podría ser solo una línea de código, pero para mí eso no parece simple. Para mí, atravesar la Lista y analizar sus elementos en la Cadena es más simple que esto.
Bartzilla
1
Esta fue mi primera impresión también. Pero después de acostumbrarme, me parece increíble. Por ejemplo, puede agregar operaciones como filter. Al final, es mucho más fácil para mí leer el código.
amra
3
En Java 8: String.join ("\ t", array)
Tomasz
El último comentario funciona solo si el contenido también es un String. Si es una lista de Integer
correos electrónicos,
Los 3 requieren el nivel 24 de API en Android.
Asad35Waheed
6

Es un O(n)algoritmo de cualquier manera (a menos que haya hecho alguna solución de subprocesos múltiples en la que separó la lista en varias sublistas, pero no creo que sea lo que está pidiendo).

Simplemente use un StringBuildercomo a continuación:

StringBuilder sb = new StringBuilder();

for (Object obj : list) {
  sb.append(obj.toString());
  sb.append("\t");
}

String finalString = sb.toString();

El StringBuilderva a ser mucho más rápido que la concatenación de cadenas, ya que no se volverá a instancias de un Stringobjeto en cada una concatenación.

Mike C.
fuente
5

En caso de que esté en Android y aún no esté usando Jack (por ejemplo, porque todavía no tiene soporte para Instant Run), y si desea tener más control sobre el formato de la cadena resultante (por ejemplo, le gustaría usar el carácter de nueva línea como el divisor de elementos), y al usar / querer usar la biblioteca StreamSupport (para usar secuencias en Java 7 o versiones anteriores del compilador), podría usar algo como esto (pongo este método en mi clase ListUtils):

public static <T> String asString(List<T> list) {
    return StreamSupport.stream(list)
            .map(Object::toString)
            .collect(Collectors.joining("\n"));
}

Y, por supuesto, asegúrese de implementar toString () en la clase de objetos de su lista.

Javad Sadeqzadeh
fuente
1
El uso reducees extremadamente ineficiente para la concatenación de cadenas. Debe hacerlo de la manera Java 8 return StreamSupport.stream(list).map(Object::toString).collect(joining("\n"))que usa internamente StringJoiner. No hay razón para que no puedas hacer esto con streamsupport también.
Stefan Zobel
Otra cosa es que su código fallaría horriblemente (debido a Optional#get) si se pasa una lista vacía.
Stefan Zobel
@StefanZobel Gracias por señalar los problemas de eficiencia y casos extremos. Modificado el código.
Javad Sadeqzadeh
3

Si no desea el último \ t después del último elemento, debe usar el índice para verificar, pero recuerde que esto solo "funciona" (es decir, es O (n)) cuando las listas implementan RandomAccess.

List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");

StringBuilder sb = new StringBuilder(list.size() * apprAvg); // every apprAvg > 1 is better than none
for (int i = 0; i < list.size(); i++) {
    sb.append(list.get(i));
    if (i < list.size() - 1) {
        sb.append("\t");
    }
}
System.out.println(sb.toString());

fuente
3

El siguiente código puede ayudarlo,

List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
String str = list.toString();
System.out.println("Step-1 : " + str);
str = str.replaceAll("[\\[\\]]", "");
System.out.println("Step-2 : " + str);

Salida:

Step-1 : [1, 2, 3]
Step-2 : 1, 2, 3
Srinivasan.S
fuente
3

Puede que no sea la mejor manera, sino la manera elegante.

Arrays.deepToString (Arrays.asList ("Prueba", "Prueba2")

import java.util.Arrays;

    public class Test {
        public static void main(String[] args) {
            System.out.println(Arrays.deepToString(Arrays.asList("Test", "Test2").toArray()));
        }
    }

Salida

[Prueba, Prueba2]

Mohan Narayanaswamy
fuente
1
Creo que esta es la mejor manera si "mejor" significa legibilidad de código. No he mirado el código, pero probablemente sea tan eficiente como el método StringBuilder (probablemente usa un StringBuilder bajo el capó), aunque no tan personalizable en la salida.
Mike Miller
3

¿Sería bueno lo siguiente?

List<String> streamValues = new ArrayList<>();
Arrays.deepToString(streamValues.toArray()));   
Beezer
fuente
3

Si está usando Eclipse Collections , puede usar el makeString()método.

ArrayList<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");

Assert.assertEquals(
    "one\ttwo\tthree",
    ArrayListAdapter.adapt(list).makeString("\t"));

Si puede convertir su ArrayLista FastList, puede deshacerse del adaptador.

Assert.assertEquals(
    "one\ttwo\tthree",
    FastList.newListWith("one", "two", "three").makeString("\t"));

Nota: Soy un committer para Eclipse Collections.

Craig P. Motlin
fuente
3
List<String> stringList = getMyListOfStrings();
StringJoiner sj = new StringJoiner(" ");
stringList.stream().forEach(e -> sj.add(e));
String spaceSeparated = sj.toString()

Pasa a la new StringJoinersecuencia de caracteres que desea utilizar como separador. Si quieres hacer un CSV:new StringJoiner(", ");

fmsf
fuente
3

En una línea: de [12,0,1,78,12] a 12 0 1 78 12

String srt= list.toString().replaceAll("\\[|\\]|,","");
Pratyush Pranjal
fuente
2
1) Ya no hay ninguna razón para usar un código como este (¡a partir de hace 4 años!). 2) Esta respuesta no agrega nada a las preguntas y respuestas.
Radiodef
5 estrellas en una línea. muchas gracias.
Atef Farouk
Esto funciona perfectamente Para cadenas separadas por comas, use: String srt = list.toString (). ReplaceAll ("\ [| \]", "");
Asad35Waheed
2

Para separar usando pestañas en lugar de usar println puedes usar print

ArrayList<String> mylist = new ArrayList<String>();

mylist.add("C Programming");
mylist.add("Java");
mylist.add("C++");
mylist.add("Perl");
mylist.add("Python");

for (String each : mylist)
{       
    System.out.print(each);
    System.out.print("\t");
}
chopss
fuente
1

Veo algunos ejemplos que dependen de recursos adicionales, pero parece que esta sería la solución más simple: (que es lo que usé en mi propio proyecto) que básicamente se está convirtiendo de una ArrayList a una Array y luego a una Lista .

    List<Account> accounts = new ArrayList<>();

   public String accountList() 
   {
      Account[] listingArray = accounts.toArray(new Account[accounts.size()]);
      String listingString = Arrays.toString(listingArray);
      return listingString;
   }
Elliander
fuente
0

Esta es una conversación bastante antigua por ahora y los comunes de Apache ahora están utilizando un StringBuilder internamente: http://commons.apache.org/lang/api/src-html/org/apache/commons/lang/StringUtils.html#line. 3045

Como sabemos, esto mejorará el rendimiento, pero si el rendimiento es crítico, entonces el método utilizado podría ser algo ineficiente. Mientras que la interfaz es flexible y permitirá un comportamiento consistente en diferentes tipos de Colección, es algo ineficiente para las Listas, que es el tipo de Colección en la pregunta original.

Baso esto en que estamos incurriendo en una sobrecarga que evitaríamos simplemente iterando a través de los elementos en un bucle for tradicional. En cambio, hay algunas cosas adicionales que suceden detrás de escena para verificar modificaciones concurrentes, llamadas a métodos, etc. Por otro lado, el bucle for mejorado dará como resultado la misma sobrecarga ya que el iterador se usa en el objeto Iterable (la Lista).

Alex VI
fuente
0

Puedes usar un Regex para esto. Esto es tan conciso como se pone

System.out.println(yourArrayList.toString().replaceAll("\\[|\\]|[,][ ]","\t"));
Vikrant Goel
fuente
-1

¿Qué tal esta función:

public static String toString(final Collection<?> collection) {
    final StringBuilder sb = new StringBuilder("{");
    boolean isFirst = true;
    for (final Object object : collection) {
        if (!isFirst)
            sb.append(',');
        else
            isFirst = false;
        sb.append(object);
    }
    sb.append('}');
    return sb.toString();
}

funciona para cualquier tipo de colección ...

desarrollador de Android
fuente