Java-Sammlungen gut drucken (toString liefert keine hübsche Ausgabe)

211

Ich möchte ein Stack<Integer>Objekt so schön drucken wie der Eclipse-Debugger (dh [1,2,3...]), aber das Drucken mit out = "output:" + stackliefert nicht dieses schöne Ergebnis.

Zur Verdeutlichung spreche ich von Javas integrierter Sammlung, damit ich sie nicht überschreiben kann toString().

Wie kann ich eine schöne druckbare Version des Stapels erhalten?

Elazar Leibovich
quelle
7
Zumindest ab Java 7 AbstractCollection@toString(und damit String + Stack) wird es bereits so gedruckt, wie Sie es möchten.
Ciro Santilli 法轮功 冠状 病 六四 事件 2

Antworten:

317

Sie können es in ein Array konvertieren und dann ausdrucken mit Arrays.toString(Object[]):

System.out.println(Arrays.toString(stack.toArray()));
Zach Langley
quelle
11
Ich mag das. Einfach, sauber. Um ehrlich zu sein, benötigt Collections auch eine toString-Methode, aber das funktioniert auch.
Tovi7
1
@ Tovi7 Dies ist wahrscheinlich nicht der Fall, da die meisten OOTB-Sammlungen bereits lesbare toString () s bereitstellen, während dies bei Arrays nicht der Fall ist.
Max Nanasy
@Boosha es dauert auch O (n) Zeit, um den Stapel in eine Zeichenfolge umzuwandeln, und O (n) Zeit, um die Zeichenfolge auf der Konsole zu drucken
Zach Langley
stack.toArray()kann sehr teuer sein, CPU, Zeit und Speicher weise. Eine Lösung, die über die ursprüngliche Sammlung / iterable iteriert, wäre wahrscheinlich weniger ressourcenintensiv.
AlikElzin-Kilaka
52
String.join(",", yourIterable);

(Java 8)

user1016765
quelle
12
yourIterable muss Iterable sein <? verlängert CharSequence>
Nathan
3
String.join (",", yourCollection.stream (). Map (o -> o.toString ()). Collect (Collectors.toList ()))
user1016765
@ user1016765 yourCollection.stream().map( o -> o.toString() ).collect( joining(",") ))ist besser, weil Sie es von links nach rechts lesen. Sie müssen nicht nach vorne schauen, um in Ihrem Gehirn zu berechnen, was mit der Zwischenliste gemacht wird
cdalxndr
18

Mit Java 8 Streams und Sammlern geht das ganz einfach:

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

Zuerst verwenden wir mapmit Object::toString, um Collection<String>einen Join-Collector zu erstellen, und verwenden dann Joining Collector, um jedes Element in der Sammlung mit einem ,Trennzeichen zu verbinden.

bsmk
quelle
22
Ich musste mich zurückhalten, um das Wort 'leicht' nicht aus der Antwort zu entfernen ;-) Collections.toString(stack)wäre einfach.
FrVaBe
Warum der Aufruf von String.format ()? Ist es nur um die eckigen Klammern zu bekommen?
Jolta
18

Die vom Apache Commons-Projekt angebotene MapUtils-Klasse bietet eine MapUtils.debugPrintMethode, mit der Ihre Karte hübsch gedruckt werden kann.

Tlavarea
quelle
Nicht, dass ich davon Wüste. Ich bin mit der Guavenbibliothek nicht besonders vertraut, aber ich wäre nicht überrascht, wenn es eine gäbe.
Tlavarea
Zumindest in Java 6 ist dies nicht erforderlich, da AbstractMap # toString dies bereits tut. Nur Karte Frage: stackoverflow.com/questions/2828252/map-to-string-in-java
Ciro Santilli 法轮功 冠状 病 六四 事件 2
12

System.out.println (Sammlung c) druckt bereits jede Art von Sammlung in lesbarem Format. Nur wenn die Sammlung benutzerdefinierte Objekte enthält, müssen Sie toString () in einer benutzerdefinierten Klasse implementieren, um Inhalte anzuzeigen.

Shekhar
quelle
12

Implementieren Sie toString () in der Klasse.

Ich empfehle den Apache Commons ToStringBuilder , um dies zu vereinfachen. Damit müssen Sie nur diese Art von Methode schreiben:

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

Um diese Art von Ausgabe zu erhalten:

Person @ 7f54 [Name = Stephen, Alter = 29]

Es gibt auch eine reflektierende Implementierung .

Chinnery
quelle
ToStringBuilder eignet sich normalerweise besser für Beans und Objekte, die Informationen enthalten, weniger für komplexe Datenstrukturen. Wenn das Stapelobjekt nicht alle gespeicherten Elemente druckt, hilft dies nicht.
Uri
2
Die Verwendung von Reflection ToStringBuilder, HashCodeBuilder und EqualsBuilder ist äußerst ineffektiv. Obwohl die Ausgabe in Ordnung ist, sind diese Klassen kaum die Leistungsspitze der Woche ...
Jan Hruby
2
Die Frage besagt ausdrücklich, dass die Klasse eine integrierte Sammlung ist, sodass toString () nicht geändert werden kann.
Rasmus Kaj
9

Ich stimme den obigen Kommentaren zum Überschreiben toString()Ihrer eigenen Klassen zu (und zur weitestgehenden Automatisierung dieses Prozesses).

Für Klassen, die Sie nicht definiert haben, können Sie ToStringHelperfür jede Bibliotheksklasse, die Sie nach Ihrem Geschmack behandeln möchten , eine Klasse mit einer überladenen Methode schreiben :

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

BEARBEITEN: Als Antwort auf den Kommentar von xukxpvfzflbbld ist hier eine mögliche Implementierung für die zuvor genannten Fälle.

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

}

Dies ist keine vollständige Implementierung, sondern nur ein Anfang.

joel.neely
quelle
7

Sie können die Klasse "Objekte" von JAVA verwenden (verfügbar seit 1.7).

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

Ausgabe: 1273, 123, 876, 897

Eine andere Möglichkeit ist die Verwendung der Klasse "MoreObjects" von Google Guave , die viele nützliche Hilfsfunktionen bietet:

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

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

Guavendokumente

Chisey88
quelle
1
Objects.toString()ruft einfach toString()die Sammlung auf. In Ihrem Beispiel funktioniert dies, weil vermutlich toString()auf der Array-gestützten Sammlung hübsch gedruckt wird.
GuyPaddock
3

Mit Apache Commons 3 möchten Sie anrufen

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

In Java8

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

oder

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

Das vorherige Beispiel wurde so geändert, dass sogar eine Sammlung mit benutzerdefinierten Objekten gedruckt wird.

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
quelle
1

Die meisten Sammlungen haben toString()heutzutage eine nützliche Java-Version (Java7 / 8). Sie müssen also keine Stream-Vorgänge ausführen, um das zu verketten, was Sie benötigen. Überschreiben Sie einfach toStringIhre Wertklasse in der Sammlung, und Sie erhalten das, was Sie benötigen.

Sowohl AbstractMap als auch AbstractCollection implementieren toString (), indem sie toString pro Element aufrufen.

Unten finden Sie eine Testklasse, um das Verhalten zu zeigen.

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
quelle
1

JSON

Eine alternative Lösung könnte darin bestehen, Ihre Sammlung in das JSON- Format zu konvertieren und den Json-String zu drucken. Der Vorteil ist ein gut formatierter und lesbarer Object-String, ohne dass der implementiert werden muss toString().

Beispiel mit Gson von 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
quelle
0

Wenn dies Ihre eigene Auflistungsklasse ist und keine eingebaute, müssen Sie die toString-Methode überschreiben. Eclipse ruft diese Funktion für alle Objekte auf, für die es keine fest verdrahtete Formatierung gibt.

Uri
quelle
Und wie formatiert Eclipse diese Klassen mit fest verdrahteter Formatierung? Das suche ich.
Elazar Leibovich
0

Seien Sie vorsichtig, wenn Sie Sop on Collection aufrufen, da dies eine ConcurrentModificationAusnahme auslösen kann . Weil die interne toStringMethode jeder Sammlung die Sammlung intern aufruft Iterator.

Harneet
quelle
0

Sollte für jede Sammlung funktionieren, außer Map, aber es ist auch einfach zu unterstützen. Ändern Sie den Code, um diese 3 Zeichen bei Bedarf als Argumente zu übergeben.

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

Sie können versuchen, mit

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

Es gibt zwei Möglichkeiten, wie Sie Ihre Arbeit vereinfachen können. 1. Importieren Sie die Gson-Bibliothek. 2. Verwenden Sie Lombok.

Beide helfen Ihnen beim Erstellen eines Strings aus einer Objektinstanz. Gson analysiert Ihr Objekt, lombok überschreibt Ihr Klassenobjekt in der String-Methode.

Ich habe ein Beispiel für Gson PrettyPrint gegeben. Ich erstelle eine Hilfsklasse zum Drucken von Objekten und zum Sammeln von Objekten. Wenn Sie Lombok verwenden, können Sie Ihre Klasse als @ToString markieren und Ihr Objekt direkt drucken.

@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
quelle