Ładne drukowanie kolekcji Java (toString nie zwraca ładnych wyników)

211

Chcę wydrukować Stack<Integer>obiekt tak ładnie, jak robi to debugger Eclipse (tj. [1,2,3...]), Ale wydrukowanie go za pomocą out = "output:" + stacknie zwraca tego miłego wyniku.

Dla wyjaśnienia mówię o wbudowanej kolekcji Java, więc nie mogę jej zastąpić toString().

Jak mogę uzyskać ładną wersję stosu do wydruku?

Elazar Leibovich
źródło
7
Przynajmniej od wersji Java 7 AbstractCollection@toString(i tym samym String + Stack) już drukuje go tak, jak chcesz.
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

Odpowiedzi:

317

Możesz przekonwertować go na tablicę, a następnie wydrukować to za pomocą Arrays.toString(Object[]):

System.out.println(Arrays.toString(stack.toArray()));
Zach Langley
źródło
11
Lubię to. Proste, czyste. Szczerze mówiąc, Kolekcje wymagają również metody toString, ale to również działa.
Tovi7
1
@ Tovi7 Prawdopodobnie nie, ponieważ większość kolekcji OOTB już udostępnia czytelne funkcje toString (), podczas gdy tablice nie.
Max Nanasy,
@Boosha zajmuje również O (n) czas na przekonwertowanie stosu na łańcuch i O (n) czas na wydrukowanie łańcucha na konsoli
Zach Langley,
stack.toArray()mogą być bardzo drogie, jeśli chodzi o procesor, czas i pamięć. rozwiązanie, które iteruje w stosunku do oryginalnej kolekcji / iterowalnej, prawdopodobnie zużywałoby mniej zasobów.
AlikElzin-kilaka
52
String.join(",", yourIterable);

(Java 8)

użytkownik1016765
źródło
12
twojaIterowalność musi być Iterowalna <? przedłuża CharSequence>
Nathan
3
String.join (",", yourCollection.stream (). Map (o -> o.toString ()). Collect (Collectors.toList ()))
user1016765
@ user1016765 yourCollection.stream().map( o -> o.toString() ).collect( joining(",") ))jest lepszy, ponieważ czytasz go od lewej do prawej, nie musisz patrzeć wstecz na przód, aby obliczyć w mózgu, co się dzieje z listą pośrednią
cdalxndr
18

Dzięki strumieniom i kolektorom Java 8 można to łatwo zrobić:

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

najpierw używamy mapz Object::toStringdo tworzenia, Collection<String>a następnie używamy łączenia kolekcjonera, aby łączyć każdy element w kolekcji ,jako separator.

bsmk
źródło
22
Musiałem się powstrzymać, aby nie usunąć słowa „łatwo” z odpowiedzi ;-) Collections.toString(stack)byłoby łatwe.
FrVaBe
Dlaczego wywołanie String.format ()? Czy chodzi tylko o nawiasy kwadratowe?
Jolta,
18

Klasa MapUtils oferowana przez projekt Apache Commons oferuje MapUtils.debugPrintmetodę, która całkiem wydrukuje twoją mapę.

tlavarea
źródło
Nie żebym o tym wiedział. Niezbyt dobrze znam bibliotekę Guava, ale nie byłbym zaskoczony, gdyby tak było.
tlavarea
Nie ma takiej potrzeby, przynajmniej w Javie 6, ponieważ AbstractMap # toString już to robi. Pytanie tylko na mapie: stackoverflow.com/questions/2828252/map-to-string-in-java
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
12

System.out.println (kolekcja c) już drukuje dowolny typ kolekcji w czytelnym formacie. Tylko jeśli kolekcja zawiera obiekty zdefiniowane przez użytkownika, należy zaimplementować metodę toString () w klasie zdefiniowanej przez użytkownika, aby wyświetlić zawartość.

Shekhar
źródło
12

Zaimplementuj toString () w klasie.

Polecam Apache Commons ToStringBuilder, aby to ułatwić. Dzięki temu wystarczy napisać ten rodzaj metody:

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

Aby uzyskać tego rodzaju dane wyjściowe:

Osoba @ 7f54 [imię = Stephen, wiek = 29]

Istnieje również refleksyjne wdrożenie .

Chinnery
źródło
ToStringBuilder jest zwykle bardziej odpowiedni dla komponentów bean i obiektów zawierających informacje, a mniej dla złożonych struktur danych. Jeśli obiekt stosu nie wydrukuje wszystkich przechowywanych elementów, to nie pomoże.
Uri
2
użycie refleksji ToStringBuilder, HashCodeBuilder i EqualsBuilder jest wysoce nieskuteczne. Chociaż wynik jest w porządku, klasy te nie są szczytem wydajności tygodnia ...
Jan Hruby
2
Pytanie wyraźnie mówi, że klasa jest kolekcją wbudowaną, więc toString () nie może być modyfikowany.
Rasmus Kaj
9

Zgadzam się z powyższymi komentarzami na temat przesłonięcia toString()własnych klas (i na temat automatyzacji tego procesu w jak największym stopniu).

W przypadku klas, których nie zdefiniowałeś, możesz napisać ToStringHelperklasę z przeciążoną metodą dla każdej klasy biblioteki, którą chcesz obsługiwać według własnych upodobań:

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

EDYCJA: Odpowiadając na komentarz xukxpvfzflbbld, oto możliwe wdrożenie dla wyżej wymienionych przypadków.

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

}

To nie jest pełna implementacja, ale tylko starter.

joel.neely
źródło
7

Możesz użyć klasy „Objects” z JAVA (która jest dostępna od 1.7)

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

Wyjście: 1273, 123, 876, 897

Inną możliwością jest użycie klasy „MoreObjects” od Google Guave , która zapewnia wiele przydatnych funkcji pomocniczych:

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

Dane wyjściowe: NameOfYourObject = [1273, 123, 876, 897]

Dokumenty Guava

Chisey88
źródło
1
Objects.toString()po prostu wywołuje toString()kolekcję. W twoim przykładzie działa to, ponieważ przypuszczalnie toString()w kolekcji wspieranej macierzą zdarza się ładnie wydrukować.
GuyPaddock,
3

Z Apache Commons 3 chcesz zadzwonić

StringUtils.join(myCollection, ",")
JRA_TLL
źródło
3

W Javie 8

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

lub

//to print with commas
stack.forEach(
    (ele) -> {
        System.out.print(ele + ",");
    }
);
JOG
źródło
1

Właśnie zmodyfikowałem poprzedni przykład, aby wydrukować nawet kolekcję zawierającą obiekty zdefiniowane przez użytkownika.

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
źródło
1

obecnie większość kolekcji jest przydatna toString()w Javie (Java 7/8). Nie ma więc potrzeby wykonywania operacji strumieniowych w celu konkatenacji tego, czego potrzebujesz, wystarczy zastąpić toStringklasę wartości w kolekcji i uzyskać to, czego potrzebujesz.

zarówno AbstractMap, jak i AbstractCollection implementują toString (), wywołując toString dla każdego elementu.

poniżej znajduje się klasa testowa pokazująca zachowanie.

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
źródło
1

JSON

Alternatywnym rozwiązaniem może być konwersja kolekcji w formacie JSON i wydrukowanie ciągu Json. Zaletą jest dobrze sformatowany i czytelny ciąg obiektowy bez potrzeby implementowania toString().

Przykład korzystania z Google Gson :

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
źródło
0

Jeśli jest to Twoja własna klasa kolekcji, a nie wbudowana, musisz przesłonić jej metodę toString. Eclipse wywołuje tę funkcję dla wszystkich obiektów, dla których nie ma wbudowanego formatowania.

Uri
źródło
A w jaki sposób Eclipse formatuje te klasy z formatowaniem przewodowym? Właśnie tego szukam.
Elazar Leibovich
0

Zachowaj ostrożność, dzwoniąc do Sop na Collection, może rzucić ConcurrentModificationwyjątek. Ponieważ wewnętrznie toStringmetoda każdej kolekcji wywołuje wewnętrznie Iteratorkolekcję.

Harneet
źródło
0

Powinien działać z każdą kolekcją oprócz Map, ale jest również łatwy do obsługi. W razie potrzeby zmodyfikuj kod, aby przekazać te 3 znaki jako argumenty.

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();
}
Wyświetlana nazwa
źródło
0

Możesz spróbować użyć

org.apache.commons.lang3.builder.ToStringBuilder.reflectionToString(yourCollection);
użytkownik1016765
źródło
0

Istnieją dwa sposoby uproszczenia pracy. 1. zaimportuj bibliotekę Gson. 2. użyj Lombok.

Oba z nich pomagają stworzyć String z instancji obiektu. Gson parsuje twój obiekt, lombok zastąpi twój obiekt klasy na metodę Strings.

Podaję przykład Gson prettyPrint, tworzę klasę pomocniczą do drukowania obiektów i kolekcji obiektów. Jeśli używasz lombok, możesz oznaczyć swoją klasę jako @ToString i wydrukować swój obiekt bezpośrednio.

@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
źródło