Impresión de colecciones de Java de forma agradable (toString no devuelve resultados bonitos)

211

Deseo imprimir un Stack<Integer>objeto tan bien como lo hace el depurador Eclipse (es decir [1,2,3...]), pero imprimirlo out = "output:" + stackno devuelve este buen resultado.

Solo para aclarar, estoy hablando de la colección incorporada de Java, así que no puedo anularla toString().

¿Cómo puedo obtener una buena versión imprimible de la pila?

Elazar Leibovich
fuente
77
Al menos a partir de Java 7, AbstractCollection@toString(y por lo tanto String + Stack) ya lo imprime como lo desea.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

317

Puede convertirlo en una matriz y luego imprimirlo con Arrays.toString(Object[]):

System.out.println(Arrays.toString(stack.toArray()));
Zach Langley
fuente
11
Me gusta. Simple, limpio Para ser honesto, Collections también necesita un método toString, pero esto también funciona.
Tovi7
1
@ Tovi7 Probablemente no, porque la mayoría de las Colecciones OOTB ya proporcionan toString () s legibles, mientras que las matrices no.
Max Nanasy
@Boosha también toma tiempo O (n) para convertir la pila en una cadena y tiempo O (n) para imprimir la cadena en la consola
Zach Langley
stack.toArray()puede ser muy costoso, cpu, tiempo y memoria sabio. Una solución que itera sobre la colección original / iterable probablemente consumiría menos recursos.
AlikElzin-kilaka
52
String.join(",", yourIterable);

(Java 8)

usuario1016765
fuente
12
yourIterable tiene que ser Iterable <? extiende CharSequence>
Nathan
3
String.join (",", yourCollection.stream (). Map (o -> o.toString ()). Collect (Collectors.toList ()))
usuario1016765
@ user1016765 yourCollection.stream().map( o -> o.toString() ).collect( joining(",") ))es mejor porque lo lees de izquierda a derecha, no necesitas mirar hacia atrás para calcular en tu cerebro lo que se hace con la lista intermedia
cdalxndr
18

Con java 8 streams y recopiladores se puede hacer fácilmente:

String format(Collection<?> c) {
  String s = c.stream().map(Object::toString).collect(Collectors.joining(","));
  return String.format("[%s]", s);
}

primero usamos mapcon Object::toStringpara crear Collection<String>y luego usamos unir colector para unir cada elemento de la colección con un ,delimitador.

bsmk
fuente
22
Tuve que contenerme para no eliminar la palabra 'fácilmente' de la respuesta ;-) Collections.toString(stack)sería fácil.
Viernes
¿Por qué la llamada a String.format ()? ¿Es solo para obtener los corchetes?
Jolta
18

La clase MapUtils ofrecida por el proyecto Apache Commons ofrece un MapUtils.debugPrintmétodo que imprimirá bastante su mapa.

tlavarea
fuente
No que yo sepa. No estoy terriblemente familiarizado con la biblioteca de Guava, pero no me sorprendería si la hubiera.
tlavarea
No es necesario para esto, al menos en Java 6, porque AbstractMap # toString ya lo hace. Pregunta de solo mapa: stackoverflow.com/questions/2828252/map-to-string-in-java
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
12

System.out.println (Colección c) ya imprime cualquier tipo de colección en formato legible. Solo si la colección contiene objetos definidos por el usuario, debe implementar toString () en la clase definida por el usuario para mostrar el contenido.

Shekhar
fuente
12

Implemente toString () en la clase.

Recomiendo el Apache Commons ToStringBuilder para hacer esto más fácil. Con él, solo tienes que escribir este tipo de método:

public String toString() {
     return new ToStringBuilder(this).
       append("name", name).
       append("age", age).
       toString(); 
}

Para obtener este tipo de salida:

Persona @ 7f54 [nombre = Stephen, edad = 29]

También hay una implementación reflexiva .

Chinnery
fuente
ToStringBuilder suele ser más aplicable para beans y objetos que transportan información, menos para estructuras de datos complejas. Si el objeto de pila no imprime todos los elementos almacenados, esto no ayudará.
Uri
2
El uso de la reflexión ToStringBuilder, HashCodeBuilder y EqualsBuilder es altamente ineficaz. Aunque la salida está bien, estas clases no son el pico de rendimiento de la semana ...
Jan Hruby
2
La pregunta dice explícitamente que la clase es una colección incorporada, por lo que toString () no se puede modificar.
Rasmus Kaj
9

Estoy de acuerdo con los comentarios anteriores sobre anular toString()en sus propias clases (y sobre automatizar ese proceso tanto como sea posible).

Para las clases que no definió, puede escribir una ToStringHelperclase con un método sobrecargado para cada clase de biblioteca que desee manejar según sus propios gustos:

public class ToStringHelper {
    //... instance configuration here (e.g. punctuation, etc.)
    public toString(List m) {
        // presentation of List content to your liking
    }
    public toString(Map m) {
        // presentation of Map content to your liking
    }
    public toString(Set m) {
        // presentation of Set content to your liking
    }
    //... etc.
}

EDITAR: respondiendo al comentario de xukxpvfzflbbld, aquí hay una posible implementación para los casos mencionados anteriormente.

package com.so.demos;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class ToStringHelper {

    private String separator;
    private String arrow;

    public ToStringHelper(String separator, String arrow) {
        this.separator = separator;
        this.arrow = arrow;
    }

   public String toString(List<?> l) {
        StringBuilder sb = new StringBuilder("(");
        String sep = "";
        for (Object object : l) {
            sb.append(sep).append(object.toString());
            sep = separator;
        }
        return sb.append(")").toString();
    }

    public String toString(Map<?,?> m) {
        StringBuilder sb = new StringBuilder("[");
        String sep = "";
        for (Object object : m.keySet()) {
            sb.append(sep)
              .append(object.toString())
              .append(arrow)
              .append(m.get(object).toString());
            sep = separator;
        }
        return sb.append("]").toString();
    }

    public String toString(Set<?> s) {
        StringBuilder sb = new StringBuilder("{");
        String sep = "";
        for (Object object : s) {
            sb.append(sep).append(object.toString());
            sep = separator;
        }
        return sb.append("}").toString();
    }

}

Esta no es una implementación completa, sino solo un comienzo.

joel.neely
fuente
7

Puede usar la clase "Objetos" de JAVA (que está disponible desde 1.7)

Collection<String> myCollection = Arrays.asList("1273","123","876","897");
Objects.toString(myCollection);

Salida: 1273, 123, 876, 897

Otra posibilidad es usar la clase "MoreObjects" de Google Guave , que proporciona muchas funciones útiles de ayuda:

MoreObjects.toStringHelper(this).add("NameOfYourObject", myCollection).toString());

Salida: NameOfYourObject = [1273, 123, 876, 897]

Documentos de guayaba

Chisey88
fuente
1
Objects.toString()solo llama toString()a la colección. En su ejemplo, esto funciona porque presumiblemente toString()en la colección respaldada por arreglos se imprime muy bien.
GuyPaddock
3

Con Apache Commons 3 , quieres llamar

StringUtils.join(myCollection, ",")
JRA_TLL
fuente
3

En Java8

//will prints each element line by line
stack.forEach(System.out::println);

o

//to print with commas
stack.forEach(
    (ele) -> {
        System.out.print(ele + ",");
    }
);
YOGUI
fuente
1

Acaba de modificar el ejemplo anterior para imprimir incluso colecciones que contienen objetos definidos por el usuario.

public class ToStringHelper {

    private  static String separator = "\n";

    public ToStringHelper(String seperator) {
        super();
        ToStringHelper.separator = seperator;

    }

    public  static String toString(List<?> l) {
        StringBuilder sb = new StringBuilder();
        String sep = "";
        for (Object object : l) {
            String v = ToStringBuilder.reflectionToString(object);
            int start = v.indexOf("[");
            int end = v.indexOf("]");
            String st =  v.substring(start,end+1);
            sb.append(sep).append(st);
            sep = separator;
        }
        return sb.toString();
    }

    public static String toString(Map<?,?> m) {
        StringBuilder sb = new StringBuilder();
        String sep = "";
        for (Object object : m.keySet()) {
            String v = ToStringBuilder.reflectionToString(m.get(object));
            int start = v.indexOf("[");
            int end = v.indexOf("]");
            String st =  v.substring(start,end+1);
            sb.append(sep).append(st);
            sep = separator;
        }
        return sb.toString();
    }

    public static String toString(Set<?> s) {
        StringBuilder sb = new StringBuilder();
        String sep = "";
        for (Object object : s) {
            String v = ToStringBuilder.reflectionToString(object);
            int start = v.indexOf("[");
            int end = v.indexOf("]");
            String st =  v.substring(start,end+1);
            sb.append(sep).append(st);
            sep = separator;
        }
        return sb.toString();
    }

    public static void print(List<?> l) {
        System.out.println(toString(l));    
    }
    public static void print(Map<?,?> m) {
        System.out.println(toString(m));    
    }
    public static void print(Set<?> s) {
        System.out.println(toString(s));    
    }

}
Shekhar
fuente
1

La mayoría de las colecciones tienen una utilidad toString()en Java en estos días (Java7 / 8). Por lo tanto, no es necesario realizar operaciones continuas para concatenar lo que necesita, simplemente anule toStringsu clase de valor en la colección y obtendrá lo que necesita.

tanto AbstractMap y AbstractCollection implementan toString () llamando a toString por elemento.

a continuación hay una clase de prueba para mostrar el comportamiento.

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

public class ToString {
  static class Foo {
    int i;
    public Foo(int i) { this.i=i; }
    @Override
    public String toString() {
        return "{ i: " + i + " }";
    }
  }
  public static void main(String[] args) {
    List<Foo> foo = new ArrayList<>();
    foo.add(new Foo(10));
    foo.add(new Foo(12));
    foo.add(new Foo(13));
    foo.add(new Foo(14));
    System.out.println(foo.toString());
    // prints: [{ i: 10 }, { i: 12 }, { i: 13 }, { i: 14 }]

    Map<Integer, Foo> foo2 = new HashMap<>();
    foo2.put(10, new Foo(10));
    foo2.put(12, new Foo(12));
    foo2.put(13, new Foo(13));
    foo2.put(14, new Foo(14));
    System.out.println(foo2.toString());
    // prints: {10={ i: 10 }, 12={ i: 12 }, 13={ i: 13 }, 14={ i: 14 }}
  }
}
Alex
fuente
1

JSON

Una solución alternativa podría ser convertir su colección en formato JSON e imprimir Json-String. La ventaja es un Object-String bien formateado y legible sin necesidad de implementar el toString().

Ejemplo usando el Gson de Google :

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

...

    printJsonString(stack);

...
public static void printJsonString(Object o) {
    GsonBuilder gsonBuilder = new GsonBuilder();
    /*
     * Some options for GsonBuilder like setting dateformat or pretty printing
     */
    Gson gson = gsonBuilder.create();
    String json= gson.toJson(o);
    System.out.println(json);
}
tobsob
fuente
0

Si esta es su propia clase de colección en lugar de una clase integrada, debe anular su método toString. Eclipse llama a esa función para cualquier objeto para el que no tiene un formato cableado.

Uri
fuente
¿Y cómo eclipse formatea esas clases con el formato cableado? Eso es lo que estoy buscando.
Elazar Leibovich
0

Tenga cuidado al llamar a Sop en Collection, puede lanzar ConcurrentModificationException. Porque el toStringmétodo interno de cada Colección internamente llama Iteratora la Colección.

Harneet
fuente
0

Debería funcionar para cualquier colección excepto Map, pero también es fácil de soportar. Modifique el código para pasar estos 3 caracteres como argumentos si es necesario.

static <T> String seqToString(Iterable<T> items) {
    StringBuilder sb = new StringBuilder();
    sb.append('[');
    boolean needSeparator = false;
    for (T x : items) {
        if (needSeparator)
            sb.append(' ');
        sb.append(x.toString());
        needSeparator = true;
    }
    sb.append(']');
    return sb.toString();
}
Nombre para mostrar
fuente
0

Puedes intentar usar

org.apache.commons.lang3.builder.ToStringBuilder.reflectionToString(yourCollection);
usuario1016765
fuente
0

Hay dos formas de simplificar su trabajo. 1. importar la biblioteca de Gson. 2. usa Lombok.

Ambos te ayudan a crear String desde la instancia del objeto. Gson analizará su objeto, lombok anulará su objeto de clase al método String.

Pongo un ejemplo sobre Gson prettyPrint, creo una clase auxiliar para imprimir objetos y colecciones de objetos. Si está utilizando lombok, puede marcar su clase como @ToString e imprimir su objeto directamente.

@Scope(value = "prototype")
@Component
public class DebugPrint<T> {
   public String PrettyPrint(T obj){
      Gson gson = new GsonBuilder().setPrettyPrinting().create();
      return gson.toJson(obj);
   }
   public String PrettyPrint(Collection<T> list){
      Gson gson = new GsonBuilder().setPrettyPrinting().create();
      return list.stream().map(gson::toJson).collect(Collectors.joining(","));
   }

}

Dongcai Huang
fuente