Imprimer correctement les collections Java (toString ne renvoie pas une jolie sortie)


211

Je souhaite imprimer un Stack<Integer>objet aussi bien que le débogueur Eclipse (c'est-à-dire [1,2,3...]) mais l'imprimer avec out = "output:" + stackne renvoie pas ce joli résultat.

Juste pour clarifier, je parle de la collection intégrée de Java, donc je ne peux pas la remplacer toString().

Comment puis-je obtenir une belle version imprimable de la pile?


7
Au moins à partir de Java 7, AbstractCollection@toString(et donc String + Stack) l'imprime déjà comme vous le souhaitez.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:


317

Vous pouvez le convertir en un tableau puis l'imprimer avec Arrays.toString(Object[]):

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

11
Je l'aime. Simple, propre. Pour être honnête, Collections a également besoin d'une méthode toString, mais cela fonctionne également.
Tovi7

1
@ Tovi7 Ce n'est probablement pas le cas car la plupart des collections OOTB fournissent déjà des toString () lisibles, contrairement aux tableaux.
Max Nanasy

@Boosha, il faut également O (n) pour convertir la pile en chaîne et O (n) pour imprimer la chaîne sur la console
Zach Langley

stack.toArray()pourrait être très cher, cpu, temps et mémoire. une solution qui itère sur la collection d'origine / itérable serait probablement moins consommatrice de ressources.
AlikElzin-kilaka

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

(Java 8)


12
yourIterable doit être Iterable <? étend CharSequence>
Nathan

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

@ user1016765 yourCollection.stream().map( o -> o.toString() ).collect( joining(",") ))est mieux car vous le lisez de gauche à droite, vous n'avez pas besoin de regarder en arrière pour calculer dans votre cerveau ce qui est fait avec la liste intermédiaire
cdalxndr

18

Avec les flux et les collecteurs java 8, cela peut être fait facilement:

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

nous utilisons d'abord mapavec Object::toStringpour créer Collection<String>, puis utilisons le collecteur de jonction pour joindre chaque élément de la collection avec ,comme délimiteur.


22
Je devais me retenir de ne pas supprimer le mot «facilement» de la réponse ;-) Collections.toString(stack)serait facile.
FrVaBe

Pourquoi l'appel à String.format ()? Est-ce juste pour obtenir les crochets?
Jolta

18

La classe MapUtils offerte par le projet Apache Commons propose une MapUtils.debugPrintméthode qui imprimera joliment votre carte.


Pas que je sache de. Je ne connais pas très bien la bibliothèque Guava mais je ne serais pas surpris s'il y en avait.
tlavarea


12

System.out.println (Collection c) imprime déjà tout type de collection au format lisible. Uniquement si la collection contient des objets définis par l'utilisateur, vous devez implémenter toString () dans la classe définie par l'utilisateur pour afficher le contenu.


12

Implémentez toString () sur la classe.

Je recommande Apache Commons ToStringBuilder pour rendre cela plus facile. Avec lui, il vous suffit d'écrire ce genre de méthode:

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

Afin d'obtenir ce type de sortie:

Personne @ 7f54 [nom = Stephen, âge = 29]

Il existe également une mise en œuvre réfléchie .


ToStringBuilder est généralement plus applicable aux beans et aux objets qui transportent des informations, moins aux structures de données complexes. Si l'objet de pile n'imprime pas tous les éléments stockés, cela n'aidera pas.
Uri

2
l'utilisation de la réflexion ToStringBuilder, HashCodeBuilder et EqualsBuilder est très inefficace. Bien que la sortie est ok, ces classes ne sont guère le pic de performance de la semaine ...
Jan Hruby

2
La question dit explicitement que la classe est une collection intégrée, donc toString () ne peut pas être modifié.
Rasmus Kaj


9

Je suis d'accord avec les commentaires ci-dessus sur le remplacement toString()de vos propres classes (et sur l'automatisation de ce processus autant que possible).

Pour les classes que vous n'avez pas définies , vous pouvez écrire une ToStringHelperclasse avec une méthode surchargée pour chaque classe de bibliothèque que vous souhaitez gérer selon vos goûts:

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: En réponse au commentaire de xukxpvfzflbbld, voici une implémentation possible pour les cas mentionnés précédemment.

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

}

Ce n'est pas une implémentation complète, mais juste un démarreur.


7

Vous pouvez utiliser la classe "Objects" de JAVA (disponible depuis la 1.7)

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

Sortie: 1273, 123, 876, 897

Une autre possibilité consiste à utiliser la classe "MoreObjects" de Google Guave , qui fournit de nombreuses fonctions d'aide utiles:

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

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

Documents sur la goyave


1
Objects.toString()fait juste appel toString()à la collection. Dans votre exemple, cela fonctionne parce que, vraisemblablement, toString()sur la collection basée sur un tableau, il est assez joli d'imprimer.
GuyPaddock

3

Avec Apache Commons 3 , vous souhaitez appeler

StringUtils.join(myCollection, ",")

3

Dans Java8

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

ou

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

1

Juste modifié l'exemple précédent pour imprimer même une collection contenant des objets définis par l'utilisateur.

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 plupart des collections ont une utilité toString()en java de nos jours (Java7 / 8). Il n'est donc pas nécessaire de faire des opérations de flux pour concaténer ce dont vous avez besoin, il suffit de remplacertoString votre classe de valeur dans la collection et vous obtenez ce dont vous avez besoin.

à la fois AbstractMap et AbstractCollection mettre en œuvre toString () en appelant toString par élément.

ci-dessous est une classe de test pour montrer le comportement.

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

Une solution alternative pourrait être de convertir votre collection au format JSON et d'imprimer la chaîne Json. L'avantage est une chaîne d'objet bien formatée et lisible sans avoir besoin d'implémenter le toString().

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

0

S'il s'agit de votre propre classe de collection plutôt que d'une classe intégrée, vous devez remplacer sa méthode toString. Eclipse appelle cette fonction pour tous les objets pour lesquels elle n'a pas de formatage câblé.


Et comment eclipse formate-t-il ces classes avec une mise en forme câblée? Voilà ce que je recherche.
Elazar Leibovich

0

Soyez prudent lorsque vous appelez Sop on Collection, cela peut lever ConcurrentModificationException. Parce que la toStringméthode interne de chaque collection appelle en interne Iteratorla collection.


0

Devrait fonctionner pour n'importe quelle collection sauf Map, mais il est également facile à prendre en charge. Modifiez le code pour passer ces 3 caractères en arguments si nécessaire.

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

Vous pouvez essayer d'utiliser

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

0

Il existe deux façons de simplifier votre travail. 1. importez la bibliothèque Gson. 2. utilisez Lombok.

Les deux vous aident à créer une chaîne à partir d'une instance d'objet. Gson analysera votre objet, lombok remplacera votre objet classe par la méthode toString.

Je mets un exemple sur Gson prettyPrint, je crée une classe d'assistance pour imprimer un objet et une collection d'objets. Si vous utilisez lombok, vous pouvez marquer votre classe comme @ToString et imprimer votre objet directement.

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

}

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.