159

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.

gabuzo
  • 7,378
  • 4
  • 28
  • 36
space_monkey
  • 1,712
  • 2
  • 11
  • 12
  • 2
    possible duplicate of [Map to String in Java](http://stackoverflow.com/questions/2828252/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. – Ciro Santilli OurBigBook.com Mar 02 '15 at 10:47

16 Answers16

323
Arrays.toString(map.entrySet().toArray())
Arkadiusz Cieśliński
  • 5,307
  • 3
  • 23
  • 19
  • 14
    Not so good if you have `Map>`, then you'll get this type of sting: `[test={test=[D@3995ebd8, 2=[D@26fa5067, 3=[D@1d956d37, 4=[D@2cead81, 5=[D@14934d2b}...]` – zygimantus Jan 16 '16 at 13:23
  • 5
    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. – Shadoninja Feb 08 '19 at 14:39
76

Have a look at the Guava library:

Joiner.MapJoiner mapJoiner = Joiner.on(",").withKeyValueSeparator("=");
System.out.println(mapJoiner.join(map));
JeanValjean
  • 17,172
  • 23
  • 113
  • 157
Mark Wigmans
  • 761
  • 5
  • 3
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.

adarshr
  • 61,315
  • 23
  • 138
  • 167
  • 7
    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. – OoDeLally Feb 18 '20 at 09:11
  • 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. – Happy Aug 07 '23 at 23:43
44

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>
tbraun
  • 2,636
  • 31
  • 26
30

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)

Thamme Gowda
  • 11,249
  • 5
  • 50
  • 57
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}
AlikElzin-kilaka
  • 34,335
  • 35
  • 194
  • 277
  • 3
    You can also add nice indenting by adding the setPrettyPrinting() to a GsonBuilder: new GsonBuilder().setPrettyPrinting().create() – Keith Tyler May 11 '21 at 18:44
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);
charles-allen
  • 3,891
  • 2
  • 23
  • 35
Nikita Koksharov
  • 10,283
  • 1
  • 62
  • 71
  • 2
    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 `, ` – charles-allen Sep 14 '17 at 15:29
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 "";
    }
    
MattG
  • 5,589
  • 5
  • 36
  • 52
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 :-)

parasietje
  • 1,529
  • 8
  • 36
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

Cratylus
  • 52,998
  • 69
  • 209
  • 339
  • 1
    his keys and values are strings; I think he got the toString method covered tbh. – Tom Apr 12 '12 at 08:53
  • You are right, but perhaps he copied a trivial example to make his point.But I update answer – Cratylus Apr 12 '12 at 08:54
  • Yes, I should have indicated that my map is Map. 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. – space_monkey Apr 12 '12 at 08:58
  • 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`. – Abdull Sep 26 '13 at 10:26
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
  • 8,588
  • 1
  • 25
  • 27
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.

user2171669
  • 745
  • 1
  • 12
  • 22
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);
}
Pritam Banerjee
  • 17,953
  • 10
  • 93
  • 108
Tomasz Stępnik
  • 87
  • 1
  • 11
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.

Community
  • 1
  • 1
Sirs
  • 1,277
  • 17
  • 30
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
Abdull
  • 26,371
  • 26
  • 130
  • 172
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...),..}, ... }

user1485864
  • 499
  • 6
  • 18