Stampa in modo grazioso una mappa in Java


136

Sto cercando un modo carino per stampare graziosamente a Map.

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

Voglio più libertà nei valori di immissione della mia mappa e sto cercando qualcosa di più simile a questo: key1="value1", key2="value2", key3="value3"

Ho scritto questo piccolo pezzo di codice:

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

Ma sono sicuro che esiste un modo più elegante e conciso per farlo.


1
possibile duplicato di Map to String in Java perché il formato richiesto qui e quello di System.out.printlnsono troppo vicini. E se vuoi qualcosa di personalizzato, questo si riduce a "come iterare su una mappa in Java" che sicuramente ha molte altre risposte.
Ciro Santilli 2 冠状 病 六四 事件 法轮功

Risposte:


63

O metti la tua logica in una piccola classe ordinata.

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: è anche possibile inserire quella logica in un metodo di utilità.


3
Questo è un imo antipatico. Aggiungi un forte vincolo (eredità) al tuo tipo, solo per piccoli vantaggi (bella stampa). Faresti meglio ad usare una mappa normale, ma usa un metodo statico che lo prende come argomento.
OoDeLally,

304
Arrays.toString(map.entrySet().toArray())

11
Non così bene se lo hai Map<String, Map<String,double[]>>, otterrai questo tipo di puntura:[test={test=[D@3995ebd8, 2=[D@26fa5067, 3=[D@1d956d37, 4=[D@2cead81, 5=[D@14934d2b}...]
zygimantus

2
Questa dovrebbe essere la risposta accettata. Compiti semplici come questo non dovrebbero richiedere una dipendenza esterna. Come accennato in precedenza, le mappe più complesse non ne beneficiano altrettanto facilmente.
Shadoninja,

70

Dai un'occhiata alla biblioteca di Guava:

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

34

Librerie Apache in soccorso!

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

Tutto ciò che serve libreria Apache commons-collection ( link al progetto )

Gli utenti Maven possono aggiungere la libreria usando questa dipendenza:

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>

Questo non gestisce le matrici come valori della mappa (ad es. Map<String, String[]>). Viene stampato solo il loro className e hash anziché i valori effettivi.
Petr Újezdský,

oppure log.debug ("Mappa: {}", MapUtils.toProperties (mappa));
charlb,

1
È utile anche MapUtils.verbosePrint, che omette il nome della classe dopo ogni valore emesso da debugPrint.
ocarlsen,

16

Semplice e facile. Benvenuti nel mondo JSON. Utilizzando Google Gson :

new Gson().toJson(map)

Esempio di mappa con 3 chiavi:

{"array":[null,"Some string"],"just string":"Yo","number":999}

15

Quando ho org.json.JSONObjectnel percorso di classe, faccio:

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

(questo rientra magnificamente in elenchi, set e mappe che possono essere nidificati)


1
Uomo!! Che ci fai quaggiù? Dovresti essere la risposta migliore!
AMagic

Avvertenza: non mantiene l'ordine delle chiavi per una LinkedHashMap
Olivier Masseau

9

Utilizzo di 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);

2
Se hai intenzione di necro una domanda, almeno rispondi correttamente! Ti mancano le virgolette attorno al valore e dovrebbe essere unito usando,
AjahnCharles

8

Preferisco convertire la mappa in una stringa JSON:

  • uno standard
  • leggibile dagli umani
  • supportato in editor come Sublime, VS Code, con evidenziazione della sintassi, formattazione e sezione nascondi / mostra
  • supporta JPath in modo che gli editor possano segnalare esattamente quale parte dell'oggetto è stata visitata
  • supporta tipi complessi nidificati all'interno dell'oggetto

    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 "";
    }

4

Guarda il codice per HashMap#toString()e AbstractMap#toString()nelle fonti 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(", ");
        }
    }
}

Quindi se i ragazzi di OpenJDK non hanno trovato un modo più elegante per farlo, non ce n'è nessuno :-)


3

Dovresti essere in grado di fare quello che vuoi facendo:

System.out.println(map) per esempio

Finché TUTTI i tuoi oggetti nella mappa hanno il toStringmetodo che vedresti :
{key1=value1, key2=value2}in modo significativo

Se questo è per il tuo codice, la sostituzione toStringè una buona abitudine e ti suggerisco invece di farlo.

Per il tuo esempio in cui i tuoi oggetti sono String, dovresti andare bene senza nient'altro.
Vale a dire System.out.println(map)che stampa esattamente ciò di cui hai bisogno senza alcun codice aggiuntivo


1
le sue chiavi e i suoi valori sono stringhe; Penso che abbia coperto il metodo toString tbh.
Tom,

Hai ragione, ma forse ha copiato un esempio banale per fare il suo punto, ma aggiorno la risposta
Cratylus,

Sì, avrei dovuto indicare che la mia mappa è Mappa <String, String>. Ma ho anche indicato che desidero maggiore libertà nei valori della mia voce, ad esempio avere elenchi separati da virgole all'interno del valore di una voce. Quindi Map.toString () non lo farà.
space_monkey il

L'interfaccia java.util.Mapnon ha alcun contratto in merito toString(), quindi dipende Mapdall'implementazione effettiva ciò che System.out.println(map)-> PrintStream.println(map)-> String.valueOf(map)-> map.toString()causerà. Succede che il spesso usato java.util.AbstractMapfornisce una buona rappresentazione di stringhe per toString(). ... Altre Mapimplementazioni possono ricorrere a Object.toString(), il che si traduce in non così 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);
}

0

Immagino che qualcosa del genere sia più pulito e ti fornisca una maggiore flessibilità con il formato di output (basta cambiare modello):

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

So che rimuovere l'ultima virgola è brutto, ma penso che sia più pulito di alternative come quella di questa soluzione o l'utilizzo manuale di un iteratore.


0

Come soluzione rapida e sporca che sfrutta l'infrastruttura esistente, puoi racchiuderla uglyPrintedMapin a java.util.HashMap, quindi utilizzarla 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

0

Non risponde esattamente alla domanda, ma vale la pena menzionare l' @ToString annotazione di Lombodok . Se annoti con @ToStringle key / valueclassi, fare System.out.println(map)ciò restituirà qualcosa di significativo.

Funziona anche molto bene con le mappe delle mappe, ad esempio: Map<MyKeyClass, Map<String, MyValueClass>>verrà stampato come

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


0

String result = objectMapper.writeValueAsString(map) - semplice come questo!

Risultato:

{"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 aggiungi Jackson JSON al tuo percorso di classe.


0

Da java 8 esiste un modo semplice per farlo con Lambda:

yourMap.keySet().forEach(key -> {
    Object obj = yourMap.get(key);
    System.out.println( obj);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.