Pretty-print a Map in Java
Asked Answered
T

16

162

I am looking for a nice way to pretty-print a Map.

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

I want more freedom in my map entry values and am looking for something more like this: key1="value1", key2="value2", key3="value3"

I wrote this little piece of code:

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

But I am sure there is a more elegant and concise way to do this.

Telfer answered 12/4, 2012 at 8:48 Comment(1)
possible duplicate of Map to String in Java because the format requested here and that of System.out.println are too close. And if you want something custom, this boils down to "how to iterate over a map in Java" which certainly has many other answers.Doubles
M
65

Or put your logic into a tidy little class.

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

    }
}

Usage:

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

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

Note: You can also put that logic into a utility method.

Moskow answered 12/4, 2012 at 8:57 Comment(2)
This is an antipattern imho. You add a strong constraint (inheritance) to your type, just for petty benefits (pretty printing). You would be better off using a regular map, but use a static method that takes it as an argument.Strand
Although this is a working answer, while usually we should not re-invent the wheel if a solution from JDK or popular 3rd party lib already exists.Falsity
I
324
Arrays.toString(map.entrySet().toArray())
Indemnity answered 28/10, 2013 at 15:35 Comment(2)
Not so good if you have Map<String, Map<String,double[]>>, then you'll get this type of sting: [test={test=[D@3995ebd8, 2=[D@26fa5067, 3=[D@1d956d37, 4=[D@2cead81, 5=[D@14934d2b}...]Aparejo
This should be the accepted answer. Simple tasks like this should not require an external dependency. As mentioned above, more complex maps don't benefit quite as easily from this.Hern
W
76

Have a look at the Guava library:

Joiner.MapJoiner mapJoiner = Joiner.on(",").withKeyValueSeparator("=");
System.out.println(mapJoiner.join(map));
Wycliffite answered 15/10, 2013 at 12:3 Comment(0)
M
65

Or put your logic into a tidy little class.

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

    }
}

Usage:

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

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

Note: You can also put that logic into a utility method.

Moskow answered 12/4, 2012 at 8:57 Comment(2)
This is an antipattern imho. You add a strong constraint (inheritance) to your type, just for petty benefits (pretty printing). You would be better off using a regular map, but use a static method that takes it as an argument.Strand
Although this is a working answer, while usually we should not re-invent the wheel if a solution from JDK or popular 3rd party lib already exists.Falsity
C
45

Apache libraries to the rescue!

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

All you need Apache commons-collections library (project link)

Maven users can add the library using this dependency:

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>
Crabtree answered 19/2, 2014 at 14:17 Comment(3)
This does not handle arrays as map's values (eg. Map<String, String[]>). Only their className and hash is printed instead of actual values.Absolve
or log.debug("Map: {}", MapUtils.toProperties(map));Panga
Also handy is MapUtils.verbosePrint, which will omit the class name after each value that debugPrint outputs.Pellmell
P
31

When I have org.json.JSONObject in the classpath, I do:

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

(this beautifully indents lists, sets and maps which may be nested)

Pontianak answered 5/6, 2015 at 6:28 Comment(2)
Man!! What are you doing down here? You should be top answer!Sammons
Warning: Does not preserve the order of keys for a LinkedHashMapAbility
D
19

Simple and easy. Welcome to the JSON world. Using Google's Gson:

new Gson().toJson(map)

Example of map with 3 keys:

{"array":[null,"Some string"],"just string":"Yo","number":999}
Deauville answered 11/6, 2015 at 6:48 Comment(1)
You can also add nice indenting by adding the setPrettyPrinting() to a GsonBuilder: new GsonBuilder().setPrettyPrinting().create()Apocope
G
16

Using 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);
Garibay answered 14/9, 2017 at 14:31 Comment(1)
If you're going to necro a question, at least answer it correctly! You're missing the quotes around the value and it should be joined using , Readymade
D
11

I prefer to convert the map to a JSON string it is:

  • a standard
  • human readable
  • supported in editors like Sublime, VS Code, with syntax highlighting, formatting and section hide/show
  • supports JPath so editors can report exactly which part of the object you have navigated to
  • supports nested complex types within the object

    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 "";
    }
    
Difficult answered 13/10, 2017 at 18:48 Comment(0)
M
4

Look at the code for HashMap#toString() and AbstractMap#toString() in the OpenJDK sources:

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

So if the guys from OpenJDK did not find a more elegant way to do this, there is none :-)

Muzzleloader answered 12/4, 2012 at 9:5 Comment(0)
E
3

You should be able to do what you want by doing:

System.out.println(map) for example

As long as ALL your objects in the map have overiden the toString method you would see:
{key1=value1, key2=value2} in a meaningfull manner

If this is for your code, then overiding toString is a good habit and I suggest you go for that instead.

For your example where your objects are Strings you should be fine without anything else.
I.e. System.out.println(map) would print exactly what you need without any extra code

Elgon answered 12/4, 2012 at 8:52 Comment(4)
his keys and values are strings; I think he got the toString method covered tbh.Vaso
You are right, but perhaps he copied a trivial example to make his point.But I update answerElgon
Yes, I should have indicated that my map is Map<String,String>. But I also indicated that I want more freedom in my entry values, for example having comma separated lists within an entry's value. So Map.toString() won't do.Telfer
Interface java.util.Map has no contract regarding toString(), so it's up to the actual Map implementation what System.out.println(map) -> PrintStream.println(map) -> String.valueOf(map) -> map.toString() will cause. It happens that the often-used java.util.AbstractMap provides a nice string representation for toString(). ... Other Map implementations may fall back to Object.toString(), which results in the not-so-informative com.example.MyMap@4e8c0de.Cowardice
R
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);
}
Rag answered 26/1, 2013 at 19:36 Comment(0)
F
1

String result = objectMapper.writeValueAsString(map) - as simple as this!

Result:

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

P.S. add Jackson JSON to your classpath.

Faustinafaustine answered 4/7, 2019 at 13:57 Comment(0)
F
1

Since java 8 there is easy way to do it with Lambda:

yourMap.keySet().forEach(key -> {
    Object obj = yourMap.get(key);
    System.out.println( obj);
}
Fencer answered 19/4, 2020 at 9:56 Comment(0)
B
0

I guess something like this would be cleaner, and provide you with more flexibility with the output format (simply change template):

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

I know having to remove the last comma is ugly, but I think it's cleaner than alternatives like the one in this solution or manually using an iterator.

Burgee answered 12/4, 2012 at 9:0 Comment(0)
C
0

As a quick and dirty solution leveraging existing infrastructure, you can wrap your uglyPrintedMap into a java.util.HashMap, then use 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
Cowardice answered 26/9, 2013 at 10:47 Comment(0)
M
0

Does not answer exactly the question, but it is worth mentioning Lombodok @ToString annotation. If you annotate with @ToString the key / value classes, then doing System.out.println(map) will return something meaningful.

It also works very well with maps-of-maps, for example: Map<MyKeyClass, Map<String, MyValueClass>> will be printed as

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

Madrigalist answered 7/9, 2018 at 8:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.