Usando Java 8 para convertir una lista de objetos en una cadena obtenida del método toString ()

206

Hay muchas cosas nuevas útiles en Java 8. Por ejemplo, puedo iterar con una secuencia sobre una lista de objetos y luego sumar los valores de un campo específico de las Objectinstancias de. P.ej

public class AClass {
  private int value;
  public int getValue() { return value; }
}

Integer sum = list.stream().mapToInt(AClass::getValue).sum();

Por lo tanto, estoy preguntando si hay alguna forma de construir un Stringque concatene la salida del toString()método de las instancias en una sola línea.

List<Integer> list = ...

String concatenated = list.stream().... //concatenate here with toString() method from java.lang.Integer class

Supongamos que listcontiene números enteros 1, 2y 3, espero que concatenatedsea "123"o "1,2,3".

mat_boy
fuente

Respuestas:

369

Una forma simple es agregar sus elementos de lista en un StringBuilder

   List<Integer> list = new ArrayList<>();
   list.add(1);
   list.add(2);
   list.add(3);

   StringBuilder b = new StringBuilder();
   list.forEach(b::append);

   System.out.println(b);

También puedes probar:

String s = list.stream().map(e -> e.toString()).reduce("", String::concat);

Explicación: el mapa convierte la secuencia Integer en secuencia String, luego se reduce como concatenación de todos los elementos.

Nota: Esto es lo normal reductionque se realiza en O (n 2 )

Para obtener un mejor rendimiento, utilice a StringBuildero mutable reductionsimilar a la respuesta de F. Böller.

String s = list.stream().map(Object::toString).collect(Collectors.joining(","));

Ref: reducción de corriente

Shail016
fuente
2
Sí, no una solución en línea, sino una solución.
mat_boy
No entendí eso. ¿Qué estás esperando?
Shail016
2
No entiendo por qué "reducción normal que se realiza en O (n2)". para mí "se parece" más a O (n) ...
datahaki
2
@datahaki No soy un chico de Java, pero supongo que la concatenación de cadenas iterativa (reducción) requiere una (re) asignación de matrices en cada iteración, lo que lo convierte en O (n ^ 2). joinen el otro lado preasignaría una matriz individual lo suficientemente grande como para almacenar la cadena final antes de llenarla, convirtiéndola en O (n).
Eli Korvigo
2
Muy buen consejo. Tal vez pueda simplificar usando la notación "::". Por lo tanto, list.stream().map(Object::toString).reduce("", String::concat). Usar map e-> e.toString()es un poco redundante.
e2a
194

Hay un colector joiningen la API. Es un método estático en Collectors.

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

No es perfecto debido a la llamada necesaria toString, pero funciona. Son posibles diferentes delimitadores.

F. Böller
fuente
77
Para completar: para que el código exacto funcione, debe hacerloimport static java.util.stream.Collectors.joining;
Mahdi
77
¿Por qué necesitamos el mapeo y el explícito 'toString'? Si el recopilador espera elementos de cadena, entonces la conversión debe invocarse implícitamente, ¿no?
Basel Shishani
10

En caso de que alguien esté tratando de hacer esto sin Java 8, hay un buen truco. List.toString () ya devuelve una colección que se ve así:

[1,2,3]

Dependiendo de sus requisitos específicos, esto se puede procesar posteriormente a lo que desee siempre que los elementos de su lista no contengan [] o,.

Por ejemplo:

list.toString().replace("[","").replace("]","") 

o si sus datos pueden contener corchetes esto:

String s=list.toString();
s = s.substring(1,s.length()-1) 

te dará un resultado bastante razonable.

Se puede crear un elemento de matriz en cada línea de esta manera:

list.toString().replace("[","").replace("]","").replaceAll(",","\r\n")

Utilicé esta técnica para hacer información sobre herramientas html de una lista en una aplicación pequeña, con algo como:

list.toString().replace("[","<html>").replace("]","</html>").replaceAll(",","<br>")

Si tiene una matriz, comience con Arrays.asList (list) .toString () en su lugar

Reconoceré totalmente el hecho de que esto no es óptimo, pero no es tan ineficiente como podría pensar y es bastante sencillo de leer y comprender. Sin embargo, es bastante inflexible; en particular, no intente separar los elementos con replaceAll si sus datos pueden contener comas y use la versión de subcadena si tiene corchetes en sus datos, pero para una matriz de números es más o menos Perfecto.

Bill K
fuente
Si bien esto generalmente funcionará, no se garantiza Listque no aplique la estructura de toString(). AbstractCollectionsin embargo, usa esta estructura por defecto, y creo que todas las Listimplementaciones de propósito general en Java SE también lo hacen. Como ejemplo de uno que no lo hace, org.springframework.util.AutoPopulatingListen la primavera no implementa toString()y así volvería por ejemplo " org.springframework.util.AutoPopulatingList@170a1".
M. Justin
Arrays.toString()sería una mejor opción que Arrays.asList(list).toString(), ya que está definido para devolver una cadena equivalente, es más conciso y no requiere la creación de objetos adicionales.
M. Justin
@ M.Justin No es un mal punto para la porción de matriz de esta respuesta, pero ¿qué pasa con una simple "Lista"? Realmente no puedes usar Arrays.toString () en eso. ¿Cómo sugeriría usar esta técnica en algo como la lista de AutoPopulating que mencionó? Quizás: nueva ArrayList (autoPopulatingList) .toString ()? O supongo que podría convertirlo en una matriz. Tal vez si se encuentra con un problema así, debe esperar que esté en 1.8 o posterior y pueda usar una de las otras respuestas en este hilo ...
Bill K
5

Las otras respuestas están bien. Sin embargo, también puede pasar Collectors.toList () como parámetro a Stream.collect () para devolver los elementos como ArrayList.

System.out.println( list.stream().map( e -> e.toString() ).collect( toList() ) );
Eddie B
fuente
Si usa System.out.print(ln) (list)usará el toString()método de los elementos para imprimir la lista. Entonces, este fragmento de código solo está repitiendo lo que sucede dentro de la toStringLista.
Shirkam
1
Debería ser Collectors.toList () a menos que esté importando static.
mwieczorek
Sí ... importé estática para el ejemplo. Dije, "Collectors.toList ()" en el cuerpo de la respuesta ...;)
Eddie B
2

StringListName = ObjectListName.stream (). Map (m -> m.toString ()) .collect (Collectors.toList ());

Rekha Ghanate
fuente
2

Hay un método en la API de cadena para esos casos de uso de "lista de cadenas de unión", ni siquiera necesita Stream.

List<String> myStringIterable = Arrays.asList("baguette", "bonjour");

String myReducedString = String.join(",", myStringIterable);

// And here you obtain "baguette,bonjour" in your myReducedString variable
Bachrc
fuente
1
List<String> list = Arrays.asList("One", "Two", "Three");
    list.stream()
            .reduce("", org.apache.commons.lang3.StringUtils::join);

O

List<String> list = Arrays.asList("One", "Two", "Three");
        list.stream()
                .reduce("", (s1,s2)->s1+s2);

Este enfoque también le permite crear un resultado de cadena a partir de una lista de objetos Ejemplo

List<Wrapper> list = Arrays.asList(w1, w2, w2);
        list.stream()
                .map(w->w.getStringValue)
                .reduce("", org.apache.commons.lang3.StringUtils::join);

Aquí la función reducir le permite tener un valor inicial al que desea agregar una nueva cadena Ejemplo:

 List<String> errors = Arrays.asList("er1", "er2", "er3");
            list.stream()
                    .reduce("Found next errors:", (s1,s2)->s1+s2);
0vint0
fuente
1

Probar ambos enfoques sugeridos en Shail016 y la respuesta bpedroso ( https://stackoverflow.com/a/24883180/2832140 ), el StringBuilder+ simple append(String)dentro de un forbucle, parece ejecutarse mucho más rápido quelist.stream().map([...] .

Ejemplo: este código recorre una Map<Long, List<Long>>compilación de una cadena json, usando list.stream().map([...]:

if (mapSize > 0) {
    StringBuilder sb = new StringBuilder("[");

    for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {

        sb.append("{\"" + entry.getKey().toString() + "\":[");
        sb.append(entry.getValue().stream().map(Object::toString).collect(Collectors.joining(",")));
    }
    sb.delete(sb.length()-2, sb.length());
    sb.append("]");
    System.out.println(sb.toString());
}

En mi máquina virtual de desarrollo, junit generalmente demora entre 0.35 y 1.2 segundos para ejecutar la prueba. Si bien, utilizando este código siguiente, tarda entre 0,15 y 0,33 segundos:

if (mapSize > 0) {
    StringBuilder sb = new StringBuilder("[");

    for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {

        sb.append("{\"" + entry.getKey().toString() + "\":[");

        for (Long tid : entry.getValue()) {
            sb.append(tid.toString() + ", ");
        }
        sb.delete(sb.length()-2, sb.length());
        sb.append("]}, ");
    }
    sb.delete(sb.length()-2, sb.length());
    sb.append("]");
    System.out.println(sb.toString());
}
Lorenzo Cipriani
fuente
0

¿Podemos probar esto?

public static void main(String []args){
        List<String> stringList = new ArrayList<>();
        for(int i=0;i< 10;i++){
            stringList.add(""+i);
        }
        String stringConcated = String.join(",", stringList);
        System.out.println(stringConcated);

    }
Kumar Abhishek
fuente
0
String actual = list.stream().reduce((t, u) -> t + "," + u).get();
Joe Almore
fuente
77
Por lo general, es una buena idea agregar una explicación a su publicación que hable sobre cómo funciona el código. Esto permite a los nuevos desarrolladores comprender lo que hace el código.
Caleb Kleveter
1
@CalebKleveter reduce a Lista un solo Stringy separa cada elemento con una coma ( ,).
Joe Almore el
2
Su solución SOLO funciona si la lista es una Lista <String>. OP no especificó tal clase. En su pregunta, incluso se señala un List <Integer>. Su código no se compila en ese caso.
RubioRic
0

Voy a usar la API de secuencias para convertir una secuencia de enteros en una sola cadena. El problema con algunas de las respuestas proporcionadas es que producen un tiempo de ejecución O (n ^ 2) debido a la construcción de cadenas. Una mejor solución es usar un StringBuilder y luego unir las cadenas como el paso final.

//              Create a stream of integers 
    String result = Arrays.stream(new int[]{1,2,3,4,5,6 })                
            // collect into a single StringBuilder
            .collect(StringBuilder::new, // supplier function
                    // accumulator - converts cur integer into a string and appends it to the string builder
                    (builder, cur) -> builder.append(Integer.toString(cur)),
                    // combiner - combines two string builders if running in parallel
                    StringBuilder::append) 
            // convert StringBuilder into a single string
            .toString();

Puede llevar este proceso un paso más allá al convertir la colección de objetos en una sola cadena.

// Start with a class definition
public static class AClass {
    private int value;
    public int getValue() { return value; }
    public AClass(int value) { this.value = value; }

    @Override
    public String toString() {
        return Integer.toString(value);
    }
}

// Create a stream of AClass objects
        String resultTwo = Arrays.stream(new AClass[]{
                new AClass(1),
                new AClass(2),
                new AClass(3),
                new AClass(4)
        })
                // transform stream of objects into a single string
                .collect(StringBuilder::new,
                        (builder, curObj) -> builder.append(curObj.toString()),
                        StringBuilder::append
                )
            // finally transform string builder into a single string
            .toString();
Lógica digital
fuente
-1

Con Java 8+

String s = Arrays.toString(list.stream().toArray(AClass[]::new));

No es el más eficiente, pero es una solución con una pequeña cantidad de código.

James Wierzba
fuente
-2

Además, puedes hacer así.

    List<String> list = Arrays.asList("One", "Two", "Three");
    String result = String.join(", ", list);
    System.out.println(result);
Evgenii
fuente
Su solución SOLO funciona si la lista es una Lista <String>. OP no especificó tal clase. En su pregunta, incluso se señala un List <Integer>.
RubioRic