Imprima un mapa en Java

136

Estoy buscando una buena manera de imprimir bonitas a Map.

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

Quiero más libertad en mis valores de entrada de mapa y estoy buscando algo más como esto: key1="value1", key2="value2", key3="value3"

Escribí este pequeño 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();

Pero estoy seguro de que hay una manera más elegante y concisa de hacer esto.

mono espacial
fuente
1
posible duplicado de Map to String en Java porque el formato solicitado aquí y el de System.out.printlnestán demasiado cerca. Y si desea algo personalizado, esto se reduce a "cómo iterar sobre un mapa en Java", que ciertamente tiene muchas otras respuestas.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

63

O ponga su lógica en una pequeña clase ordenada.

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: También puede poner esa lógica en un método de utilidad.

adarshr
fuente
3
Este es un imipa antipatrón. Agrega una fuerte restricción (herencia) a su tipo, solo para pequeños beneficios (impresión bonita). Sería mejor usar un mapa regular, pero use un método estático que lo tome como argumento.
OoDeLally
304
Arrays.toString(map.entrySet().toArray())
Arkadiusz Cieśliński
fuente
11
No tan bueno si usted tiene Map<String, Map<String,double[]>>, entonces obtendrá este tipo de picar:[test={test=[D@3995ebd8, 2=[D@26fa5067, 3=[D@1d956d37, 4=[D@2cead81, 5=[D@14934d2b}...]
zygimantus
2
Esta debería ser la respuesta aceptada. Tareas simples como esta no deberían requerir una dependencia externa. Como se mencionó anteriormente, los mapas más complejos no se benefician tan fácilmente de esto.
Shadoninja
70

Echa un vistazo a la biblioteca de guayaba:

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

¡Bibliotecas Apache al rescate!

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

Todo lo que necesitas Apache commons-collections library ( enlace del proyecto )

Los usuarios de Maven pueden agregar la biblioteca usando esta dependencia:

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>
tbraun
fuente
Esto no maneja las matrices como valores del mapa (p. Ej. Map<String, String[]>). Solo se imprime su className y hash en lugar de los valores reales.
Petr Újezdský
o log.debug ("Mapa: {}", MapUtils.toProperties (mapa));
charlb
1
También es útil MapUtils.verbosePrint, que omitirá el nombre de la clase después de cada valor que muestre debugPrint.
ocarlsen
16

Simple y fácil. Bienvenido al mundo JSON. Usando el Gson de Google :

new Gson().toJson(map)

Ejemplo de mapa con 3 llaves:

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

Cuando tengo org.json.JSONObjecten el classpath, hago:

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

(esto sangra maravillosamente listas, conjuntos y mapas que pueden estar anidados)

Thamme Gowda
fuente
1
¡¡Hombre!! ¿Qué estas haciendo aquí? ¡Deberías ser la mejor respuesta!
AMagic
Advertencia: no conserva el orden de las claves para un LinkedHashMap
Olivier Masseau
9

Usando 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
fuente
2
Si vas a necro una pregunta, ¡al menos respóndela correctamente! Te faltan las comillas alrededor del valor y se debe unir usando,
AjahnCharles
8

Prefiero convertir el mapa a una cadena JSON:

  • un estandar
  • legible por humanos
  • compatible con editores como Sublime, VS Code, con resaltado de sintaxis, formato y sección ocultar / mostrar
  • admite JPath para que los editores puedan informar exactamente a qué parte del objeto ha navegado
  • admite tipos complejos anidados dentro del 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
fuente
4

Mire el código para HashMap#toString()y AbstractMap#toString()en las fuentes de 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(", ");
        }
    }
}

Entonces, si los chicos de OpenJDK no encontraron una manera más elegante de hacer esto, no hay ninguno :-)

parasietje
fuente
3

Debería poder hacer lo que quiera haciendo:

System.out.println(map) por ejemplo

Mientras TODOS tus objetos en el mapa hayan anulado el toStringmétodo que verías :
{key1=value1, key2=value2}de una manera significativa

Si esto es para su código, entonces anular toStringes un buen hábito y le sugiero que lo haga.

Para su ejemplo donde están sus objetos String, debería estar bien sin nada más.
Es decir System.out.println(map), imprimiría exactamente lo que necesita sin ningún código adicional

Cratilo
fuente
1
sus claves y valores son cadenas; Creo que tiene el método toString cubierto tbh.
Tom
Tienes razón, pero tal vez copió un ejemplo trivial para hacer su punto. Pero actualizo la respuesta
Cratylus
Sí, debería haber indicado que mi mapa es Map <String, String>. Pero también indiqué que quiero más libertad en mis valores de entrada, por ejemplo tener listas separadas por comas dentro del valor de una entrada. Entonces Map.toString () no funcionará.
space_monkey
La interfaz java.util.Mapno tiene contrato con respecto toString(), por lo que depende de la Mapimplementación real lo que System.out.println(map)-> PrintStream.println(map)-> String.valueOf(map)-> map.toString()causará. Sucede que el uso frecuente java.util.AbstractMapproporciona una buena representación de cadena paratoString() . ... Otras Mapimplementaciones pueden recurrir a Object.toString(), lo que resulta en el no tan 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);
}
piedras333
fuente
0

Supongo que algo como esto sería más limpio y le proporcionaría más flexibilidad con el formato de salida (simplemente cambie la plantilla):

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

Sé que tener que eliminar la última coma es feo, pero creo que es más limpio que otras alternativas como la de esta solución o el uso manual de un iterador.

Señores
fuente
0

Como apalancamiento solución rápida y sucia infraestructura existente, se puede envolver tu uglyPrintedMapen una java.util.HashMap, a continuación, utilizar 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
fuente
0

No responde exactamente la pregunta, pero vale la pena mencionar la @ToString anotación de Lombodok . Si anota con @ToStringlas key / valueclases, entonces haciendoSystem.out.println(map) devolverá algo significativo.

También funciona muy bien con mapas de mapas, por ejemplo: Map<MyKeyClass, Map<String, MyValueClass>>se imprimirá como

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

usuario1485864
fuente
0

String result = objectMapper.writeValueAsString(map) ¡Tan simple como esto!

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

PD: agrega Jackson JSON a tu classpath.

usuario2171669
fuente
0

Desde java 8 hay una manera fácil de hacerlo con Lambda:

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