Stampare correttamente le raccolte Java (toString non restituisce un output discreto)


211

Vorrei stampare un Stack<Integer>oggetto esattamente come fa il debugger di Eclipse (ovvero [1,2,3...]) ma stamparlo con out = "output:" + stacknon restituisce questo bel risultato.

Giusto per chiarire, sto parlando della collezione integrata di Java, quindi non posso ignorarla toString().

Come posso ottenere una bella versione stampabile dello stack?


7
Almeno a partire da Java 7, AbstractCollection@toString(e quindi String + Stack) già lo stampa come lo si desidera.
Ciro Santilli 2 冠状 病 六四 事件 法轮功

Risposte:


317

È possibile convertirlo in un array e quindi stamparlo con Arrays.toString(Object[]):

System.out.println(Arrays.toString(stack.toArray()));

11
Mi piace. Semplice, pulito Ad essere onesti, anche le Collezioni hanno bisogno di un metodo toString, ma funziona anche.
Tovi7,

1
@ Tovi7 Probabilmente no perché la maggior parte delle Collezioni OOTB fornisce già leggibili aString (), mentre gli array no.
Max Nanasy,

@ Boosha ci vuole anche O (n) tempo per convertire lo stack in una stringa e O (n) tempo per stampare la stringa sulla console
Zach Langley,

stack.toArray()potrebbe essere molto costoso, CPU, tempo e memoria saggia. una soluzione che scorre sulla raccolta originale / iterabile probabilmente consumerebbe meno risorse.
AlikElzin-Kilaka,

52
String.join(",", yourIterable);

(Java 8)


12
yourIterable deve essere Iterable <? estende CharSequence>
Nathan l'

3
String.join (",", yourCollection.stream (). Map (o -> o.toString ()). Collect (Collectors.toList ()))
user1016765

@ user1016765 yourCollection.stream().map( o -> o.toString() ).collect( joining(",") ))è meglio perché lo leggi da sinistra a destra, non è necessario guardare indietro nella parte anteriore per calcolare nel tuo cervello cosa viene fatto con l'elenco intermedio
cdalxndr

18

Con java 8 stream e collezionisti si può fare facilmente:

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

prima usiamo mapconObject::toString per creare Collection<String>e poi uniamo il raccoglitore per unire ogni elemento della raccolta ,come delimitatore.


22
Ho dovuto trattenere per non rimuovere la parola "facilmente" dalla risposta ;-) Collections.toString(stack)sarebbe facile.
FrVaBe,

Perché la chiamata a String.format ()? È solo per ottenere le parentesi quadre?
Jolta,


12

System.out.println (Collection c) stampa già qualsiasi tipo di raccolta in formato leggibile. Solo se la raccolta contiene oggetti definiti dall'utente, è necessario implementare toString () nella classe definita dall'utente per visualizzare il contenuto.


12

Implementare toString () sulla classe.

Raccomando Apache Commons ToStringBuilder per rendere tutto più semplice. Con esso, devi solo scrivere questo tipo di metodo:

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

Per ottenere questo tipo di output:

Person @ 7f54 [name = Stephen, età = 29]

C'è anche un'implementazione riflessiva .


ToStringBuilder è generalmente più applicabile per bean e oggetti che trasportano informazioni, tanto meno per strutture dati complesse. Se l'oggetto pila non stampa tutti gli oggetti memorizzati, questo non aiuta.
Uri,

2
l'utilizzo della riflessione ToStringBuilder, HashCodeBuilder e EqualsBuilder è altamente inefficace. Anche se l'output è ok, queste classi non sono certo il picco delle prestazioni della settimana ...
Jan Hruby,

2
La domanda dice esplicitamente che la classe è una raccolta integrata, quindi toString () non può essere modificato.
Rasmus Kaj,


9

Concordo con i commenti di cui sopra sulla sostituzione toString()delle proprie classi (e sull'automazione di tale processo il più possibile).

Per le classi che non hai definito, potresti scrivere una ToStringHelperclasse con un metodo sovraccarico per ogni classe di libreria che desideri gestire secondo i tuoi gusti:

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

EDIT: rispondendo al commento di xukxpvfzflbbld, ecco una possibile implementazione per i casi menzionati in precedenza.

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

}

Questa non è un'implementazione completa, ma solo un antipasto.


7

Puoi usare la classe "Oggetti" di JAVA (che è disponibile dall'1.7)

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

Uscita: 1273, 123, 876, 897

Un'altra possibilità è quella di utilizzare la classe "MoreObjects" di Google Guave , che fornisce molte utili funzioni di supporto:

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

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

Documenti di guava


1
Objects.toString()chiama solo toString()la raccolta. Nel tuo esempio, questo funziona perché presumibilmente toString()sulla raccolta supportata da array sembra stampare piuttosto bene.
GuyPaddock,

3

Con Apache Commons 3 , vuoi chiamare

StringUtils.join(myCollection, ",")

3

In 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 + ",");
    }
);

1

È stato appena modificato l'esempio precedente per stampare anche la raccolta contenente oggetti definiti dall'utente.

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

}

1

la maggior parte delle raccolte ha un utile toString()in Java in questi giorni (Java7 / 8). Quindi non è necessario eseguire operazioni di streaming per concatenare ciò di cui hai bisogno, basta eseguire l'overridetoString tua classe di valore nella raccolta e ottenere ciò di cui hai bisogno.

sia AbstractMap che AbstractCollection implementano toString () chiamando toString per elemento.

di seguito è una lezione di prova per mostrare il comportamento.

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

1

JSON

Una soluzione alternativa potrebbe essere la conversione della raccolta nel formato JSON e la stampa della stringa Json. Il vantaggio è una stringa di oggetti ben formattata e leggibile senza la necessità di implementare toString().

Esempio usando Gson di 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);
}

0

Se questa è la tua classe di raccolta piuttosto che una incorporata, devi sovrascriverne il metodo toString. Eclipse chiama quella funzione per tutti gli oggetti per i quali non ha una formattazione cablata.


E in che modo eclipse formatta queste classi con la formattazione cablata? Questo è quello che sto cercando.
Elazar Leibovich,

0

Fai attenzione quando chiami Sop su Collection, può generare ConcurrentModificationun'eccezione. Perché il toStringmetodo interno di ogni Collezione chiama internamente Iteratorla Collezione.


0

Dovrebbe funzionare per qualsiasi raccolta tranne Map, ma è anche facile da supportare. Modificare il codice per passare questi 3 caratteri come argomenti, se necessario.

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

0

Puoi provare a usare

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

0

Esistono due modi per semplificare il tuo lavoro. 1. importare la libreria Gson. 2. usa Lombok.

Entrambi aiutano a creare String dall'istanza dell'oggetto. Gson analizzerà il tuo oggetto, lombok sovrascriverà il tuo oggetto di classe sul metodo String.

Ho messo un esempio su Gson prettyPrint, creo una classe di supporto per stampare oggetti e raccolte di oggetti. Se stai usando lombok, puoi contrassegnare la tua classe come @ToString e stampare direttamente il tuo oggetto.

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

}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.