Imprima um mapa em Java

136

Estou procurando uma boa maneira de imprimir bonito a Map.

map.toString() me dá: {key1=value1, key2=value2, key3=value3}

Quero mais liberdade nos meus valores de entrada no mapa e estou procurando algo mais como este: key1="value1", key2="value2", key3="value3"

Eu escrevi este pequeno pedaço de código:

StringBuilder sb = new StringBuilder();
Iterator<Entry<String, String>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
    Entry<String, String> entry = iter.next();
    sb.append(entry.getKey());
    sb.append('=').append('"');
    sb.append(entry.getValue());
    sb.append('"');
    if (iter.hasNext()) {
        sb.append(',').append(' ');
    }
}
return sb.toString();

Mas tenho certeza de que há uma maneira mais elegante e concisa de fazer isso.

space_monkey
fonte
1
possível duplicação de Map to String em Java porque o formato solicitado aqui e o de System.out.printlnestão muito próximos. E se você quiser algo personalizado, tudo se resume a "como iterar sobre um mapa em Java", que certamente tem muitas outras respostas.
Ciro Santilli #

Respostas:

63

Ou coloque sua lógica em uma turma pequena e organizada.

public class PrettyPrintingMap<K, V> {
    private Map<K, V> map;

    public PrettyPrintingMap(Map<K, V> map) {
        this.map = map;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        Iterator<Entry<K, V>> iter = map.entrySet().iterator();
        while (iter.hasNext()) {
            Entry<K, V> entry = iter.next();
            sb.append(entry.getKey());
            sb.append('=').append('"');
            sb.append(entry.getValue());
            sb.append('"');
            if (iter.hasNext()) {
                sb.append(',').append(' ');
            }
        }
        return sb.toString();

    }
}

Uso:

Map<String, String> myMap = new HashMap<String, String>();

System.out.println(new PrettyPrintingMap<String, String>(myMap));

Nota: Você também pode colocar essa lógica em um método utilitário.

adarshr
fonte
3
Este é um imho antipadrão. Você adiciona uma forte restrição (herança) ao seu tipo, apenas para pequenos benefícios (impressão bonita). Você seria melhor usando um mapa regular, mas use um método estático que o use como argumento.
OoDeLally
304
Arrays.toString(map.entrySet().toArray())
Arkadiusz Cieśliński
fonte
11
Não tão bom se você tem Map<String, Map<String,double[]>>, então você vai ter este tipo de picar:[test={test=[D@3995ebd8, 2=[D@26fa5067, 3=[D@1d956d37, 4=[D@2cead81, 5=[D@14934d2b}...]
zygimantus
2
Essa deve ser a resposta aceita. Tarefas simples como essa não devem exigir uma dependência externa. Como mencionado acima, mapas mais complexos não se beneficiam tão facilmente disso.
Shadoninja 8/02/19
70

Dê uma olhada na biblioteca do Guava:

Joiner.MapJoiner mapJoiner = Joiner.on(",").withKeyValueSeparator("=");
System.out.println(mapJoiner.join(map));
Mark Wigmans
fonte
34

Bibliotecas Apache para o resgate!

MapUtils.debugPrint(System.out, "myMap", map);

Tudo o que você precisa na biblioteca de coleções comuns do Apache ( link do projeto )

Os usuários do Maven podem adicionar a biblioteca usando esta dependência:

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>
tbraun
fonte
Isso não trata de matrizes como valores do mapa (por exemplo Map<String, String[]>). Somente o nome da classe e o hash são impressos em vez dos valores reais.
Petr Újezdský
ou log.debug ("Map: {}", MapUtils.toProperties (map));
charlb 6/02/19
1
Também é útil o MapUtils.verbosePrint, que omitirá o nome da classe após cada valor que debugPrint gera.
Ocrlsen 31/12/19
16

Simples e fácil. Bem-vindo ao mundo JSON. Usando o Gson do Google :

new Gson().toJson(map)

Exemplo de mapa com 3 teclas:

{"array":[null,"Some string"],"just string":"Yo","number":999}
AlikElzin-kilaka
fonte
15

Quando estou org.json.JSONObjectno caminho de classe, faço:

Map<String, Object> stats = ...;
System.out.println(new JSONObject(stats).toString(2));

(isso recorta listas, conjuntos e mapas maravilhosamente aninhados)

Thamme Gowda
fonte
1
Homem!! o quê você está fazendo aqui em baixo? Você deve ser a melhor resposta!
precisa saber é
Aviso: Não preserva a ordem das chaves de um LinkedHashMap
Olivier Masseau
9

Usando o Java 8 Streams:

Map<Object, Object> map = new HashMap<>();

String content = map.entrySet()
                    .stream()
                    .map(e -> e.getKey() + "=\"" + e.getValue() + "\"")
                    .collect(Collectors.joining(", "));

System.out.println(content);
Nikita Koksharov
fonte
2
Se você deseja necro uma pergunta, pelo menos responda corretamente! Você está perdendo as aspas em torno do valor e deve ser unidas utilizando,
AjahnCharles
8

Eu prefiro converter o mapa em uma string JSON:

  • uma norma
  • legível por humanos
  • suportado em editores como Sublime, VS Code, com destaque de sintaxe, formatação e seção / ocultação de seção
  • suporta JPath, para que os editores possam relatar exatamente para qual parte do objeto você navegou
  • suporta tipos complexos aninhados dentro do objeto

    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public static String getAsFormattedJsonString(Object object)
    {
        ObjectMapper mapper = new ObjectMapper();
        try
        {
            return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
        }
        catch (JsonProcessingException e)
        {
            e.printStackTrace();
        }
        return "";
    }
MattG
fonte
4

Veja o código para HashMap#toString()e AbstractMap#toString()nas fontes OpenJDK:

class java.util.HashMap.Entry<K,V> implements Map.Entry<K,V> {
       public final String toString() {
           return getKey() + "=" + getValue();
       }
   }
 class java.util.AbstractMap<K,V> {
     public String toString() {
         Iterator<Entry<K,V>> i = entrySet().iterator();
         if (! i.hasNext())
            return "{}";

        StringBuilder sb = new StringBuilder();
        sb.append('{');
        for (;;) {
            Entry<K,V> e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            sb.append(key   == this ? "(this Map)" : key);
            sb.append('=');
            sb.append(value == this ? "(this Map)" : value);
            if (! i.hasNext())
                return sb.append('}').toString();
            sb.append(", ");
        }
    }
}

Portanto, se os caras do OpenJDK não encontraram uma maneira mais elegante de fazer isso, não há :-)

parasietje
fonte
3

Você deve conseguir fazer o que deseja, fazendo:

System.out.println(map) por exemplo

Desde que TODOS os seus objetos no mapa tenham sobreposto ao toStringmétodo que você veria:
{key1=value1, key2=value2}de uma maneira significativa

Se esse for o seu código, substituir toStringé um bom hábito e sugiro que você faça isso.

Para o seu exemplo onde seus objetos estão, Stringvocê deve ficar bem sem mais nada.
Ou seja System.out.println(map), imprimiria exatamente o que você precisa sem nenhum código extra

Cratylus
fonte
1
suas chaves e valores são cadeias; Eu acho que ele conseguiu o método toString coberto tbh.
Tom
Você está certo, mas talvez ele copiou um exemplo trivial para fazer a sua resposta atualização point.But I
Cratylus
Sim, eu deveria ter indicado que meu mapa é Map <String, String>. Mas também indiquei que quero mais liberdade nos meus valores de entrada, por exemplo, ter listas separadas por vírgula no valor de uma entrada. Portanto, Map.toString () não funciona.
space_monkey
A interface java.util.Mapnão possui contrato toString(), portanto, depende da Mapimplementação real o que System.out.println(map)-> PrintStream.println(map)-> String.valueOf(map)-> map.toString()causará. Acontece que o frequentemente usado java.util.AbstractMapfornece uma boa representação de strings para toString(). ... Outras Mapimplementações podem voltar ao Object.toString(), o que resulta no não tão informativo com.example.MyMap@4e8c0de.
Abdull
2
public void printMapV2 (Map <?, ?> map) {
    StringBuilder sb = new StringBuilder(128);
    sb.append("{");
    for (Map.Entry<?,?> entry : map.entrySet()) {
        if (sb.length()>1) {
            sb.append(", ");
        }
        sb.append(entry.getKey()).append("=").append(entry.getValue());
    }
    sb.append("}");
    System.out.println(sb);
}
stones333
fonte
0

Eu acho que algo assim seria mais limpo e fornecer mais flexibilidade com o formato de saída (basta alterar o modelo):

    String template = "%s=\"%s\",";
    StringBuilder sb = new StringBuilder();
    for (Entry e : map.entrySet()) {
        sb.append(String.format(template, e.getKey(), e.getValue()));
    }
    if (sb.length() > 0) {
        sb.deleteCharAt(sb.length() - 1); // Ugly way to remove the last comma
    }
    return sb.toString();

Eu sei que ter que remover a última vírgula é feio, mas acho que é mais limpo do que alternativas como a desta solução ou manualmente usando um iterador.

Senhores
fonte
0

Como uma solução rápida e suja que utiliza a infraestrutura existente, você pode agrupá-lo uglyPrintedMapem um java.util.HashMape usá-lo toString().

uglyPrintedMap.toString(); // ugly
System.out.println( uglyPrintedMap ); // prints in an ugly manner

new HashMap<Object, Object>(jobDataMap).toString(); // pretty
System.out.println( new HashMap<Object, Object>(uglyPrintedMap) ); // prints in a pretty manner
Abdull
fonte
0

Não responde exatamente à pergunta, mas vale a pena mencionar a @ToString anotação Lombodok . Se você anotar com @ToStringas key / valueclasses, System.out.println(map)o return retornará algo significativo.

Também funciona muito bem com mapas de mapas, por exemplo: Map<MyKeyClass, Map<String, MyValueClass>>será impresso como

{MyKeyClass(properties...)={string1=MyValuesClass(properties...), string2=MyValuesCalss(properties...),..}, ... }

user1485864
fonte
0

String result = objectMapper.writeValueAsString(map) - simples assim!

Resultado:

{"2019-07-04T03:00":1,"2019-07-04T04:00":1,"2019-07-04T01:00":1,"2019-07-04T02:00":1,"2019-07-04T13:00":1,"2019-07-04T06:00":1 ...}

PS adicione Jackson JSON ao seu caminho de classe.

user2171669
fonte
0

Desde o java 8, há uma maneira fácil de fazer isso com o Lambda:

yourMap.keySet().forEach(key -> {
    Object obj = yourMap.get(key);
    System.out.println( obj);
}
Tomasz Stępnik
fonte