Imprimindo bem as coleções Java (toString não retorna uma saída bonita)

211

Desejo imprimir um Stack<Integer>objeto tão bem quanto o depurador Eclipse (isto é [1,2,3...]), mas imprimi-lo com out = "output:" + stacknão retorna esse resultado agradável.

Só para esclarecer, estou falando da coleção interna do Java, portanto não posso substituí-la toString().

Como posso obter uma boa versão imprimível da pilha?

Elazar Leibovich
fonte
7
Pelo menos a partir do Java 7, AbstractCollection@toString(e, portanto String + Stack), já o imprime como você deseja.
Ciro Santilli #

Respostas:

317

Você pode convertê-lo em uma matriz e imprimi-lo com Arrays.toString(Object[]):

System.out.println(Arrays.toString(stack.toArray()));
Zach Langley
fonte
11
Eu gosto disso. Simples, limpo. Para ser honesto, o Collections também precisa de um método toString, mas isso também funciona.
precisa saber é o seguinte
1
@ Tovi7 Provavelmente não, porque a maioria das coleções OOTB já fornece toString () legível, enquanto matrizes não.
Max Nanasy
@Boosha também leva tempo O (n) para converter a pilha para uma string e tempo O (n) para imprimir a string para o console
Zach Langley
stack.toArray()pode ser muito caro, CPU, tempo e memória. uma solução que itere sobre a coleção original / iterável provavelmente consumirá menos recursos.
AlikElzin-kilaka 14/02/19
52
String.join(",", yourIterable);

(Java 8)

user1016765
fonte
12
yourIterable tem que ser Iterable <? estende CharSequence>
Nathan
3
String.join (",", yourCollection.stream (). Map (o -> o.toString ()). Collect (Collectors.toList ()))
user1016765
@ user1016765 yourCollection.stream().map( o -> o.toString() ).collect( joining(",") ))é melhor porque você lê da esquerda para a direita, não precisa olhar para trás para calcular em seu cérebro o que é feito com a lista intermediária
cdalxndr
18

Com java 8 streams e coletores, isso pode ser feito facilmente:

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

primeiro usamos mapcom Object::toStringpara criar Collection<String>e, em seguida, usamos o coletor de junção para unir todos os itens da coleção ,como delimitador.

bsmk
fonte
22
Eu tive que me segurar para não remover a palavra 'facilmente' da resposta ;-) Collections.toString(stack)seria fácil.
FrVaBe
Por que a chamada para String.format ()? É apenas para obter os colchetes?
Jolta
18

A classe MapUtils oferecida pelo projeto Apache Commons oferece um MapUtils.debugPrintmétodo que imprime bastante o seu mapa.

tlavarea
fonte
Não que eu saiba. Não estou muito familiarizado com a biblioteca Guava, mas não ficaria surpreso se houvesse.
precisa saber é
Não há necessidade disso, pelo menos no Java 6, porque o AbstractMap # toString já o faz. Mapa única pergunta: stackoverflow.com/questions/2828252/map-to-string-in-java
Ciro Santilli郝海东冠状病六四事件法轮功
12

System.out.println (Coleção c) já imprime qualquer tipo de coleção em formato legível. Somente se a coleção contiver objetos definidos pelo usuário, você precisará implementar toString () na classe definida pelo usuário para exibir o conteúdo.

Shekhar
fonte
12

Implemente toString () na classe.

Eu recomendo o Apache Commons ToStringBuilder para facilitar isso. Com isso, você apenas precisa escrever este tipo de método:

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

Para obter esse tipo de saída:

Pessoa @ 7f54 [nome = Stephen, idade = 29]

Há também uma implementação reflexiva .

Chinnery
fonte
O ToStringBuilder é geralmente mais aplicável a beans e objetos que carregam informações, menos para estruturas de dados complexas. Se o objeto da pilha não imprimir todos os itens armazenados, isso não ajudará.
27468 Uri
2
uso de reflexão ToStringBuilder, HashCodeBuilder e EqualsBuilder são altamente ineficazes. Embora a saída é ok, essas classes são dificilmente o pico da semana desempenho ...
Jan Hruby
2
A pergunta diz explicitamente que a classe é uma coleção interna, portanto, toString () não pode ser modificado.
Rasmus Kaj 23/10
9

Concordo com os comentários acima sobre substituir toString()suas próprias classes (e sobre como automatizar esse processo o máximo possível).

Para as classes que você não definiu, você pode escrever uma ToStringHelperclasse com um método sobrecarregado para cada classe de biblioteca que deseja manipular de acordo com seu próprio gosto:

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: Respondendo ao comentário de xukxpvfzflbbld, aqui está uma possível implementação para os 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 não é uma implementação completa, mas apenas uma iniciação.

joel.neely
fonte
7

Você pode usar a classe "Objetos" do JAVA (disponível desde 1.7)

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

Saída: 1273, 123, 876, 897

Outra possibilidade é usar a classe "MoreObjects" do Google Guave , que fornece muitas funções úteis de ajuda:

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

Saída: NameOfYourObject = [1273, 123, 876, 897]

Documentos da goiaba

Chisey88
fonte
1
Objects.toString()apenas chama toString()a coleção. No seu exemplo, isso funciona porque, presumivelmente, toString()na coleção baseada em array, ocorre uma boa impressão.
GuyPaddock
3

Com o Apache Commons 3 , você deseja ligar

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

Em 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 + ",");
    }
);
YÒGÎ
fonte
1

Apenas modifiquei o exemplo anterior para imprimir uma coleção uniforme contendo objetos definidos pelo usuário.

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

a maioria das coleções possui um útil toString()em java atualmente (Java7 / 8). Portanto, não há necessidade de executar operações de fluxo para concatenar o que você precisa, basta substituir toStringsua classe de valor na coleção e obter o que você precisa.

tanto AbstractMap e AbstractCollection implementar toString () chamando toString por elemento.

abaixo está uma classe de teste para mostrar 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 }}
  }
}
Alex
fonte
1

JSON

Uma solução alternativa pode ser converter sua coleção no formato JSON e imprimir a string Json. A vantagem é uma Cadeia de Objetos bem formatada e legível, sem a necessidade de implementar o toString().

Exemplo usando o Gson do 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
fonte
0

Se esta é sua própria classe de coleção, e não uma incorporada, você precisa substituir o método toString. O Eclipse chama essa função para qualquer objeto para o qual não tenha uma formatação conectada.

Uri
fonte
E como o eclipse formata essas classes com formatação com fio? É isso que estou procurando.
Elazar Leibovich
0

Cuidado ao chamar Sop na Collection, pois isso pode gerar ConcurrentModificationException. Porque internamente o toStringmétodo de cada coleção chama internamente Iteratora coleção.

Harneet
fonte
0

Deve funcionar para qualquer coleção Map, exceto , mas também é fácil de suportar. Modifique o código para passar esses 3 caracteres como argumentos, se necessário.

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();
}
Nome em Exibição
fonte
0

Você pode tentar usar

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

Existem duas maneiras de simplificar seu trabalho. 1. importe a biblioteca Gson. 2. use Lombok.

Ambos ajudam a criar String a partir da instância do objeto. O Gson analisará seu objeto, o lombok substituirá o objeto de classe no métodoString.

Eu coloquei um exemplo sobre o Gson prettyPrint, crio uma classe auxiliar para imprimir objetos e coleção de objetos. Se você estiver usando o lombok, poderá marcar sua classe como @ToString e imprimir seu objeto diretamente.

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