Досить роздрукувати карту на Java


136

Я шукаю хороший спосіб красивого друку Map.

map.toString() дає мені: {key1=value1, key2=value2, key3=value3}

Я хочу більше свободи в моїх вхідних значеннях на карті і шукаю щось подібне: key1="value1", key2="value2", key3="value3"

Я написав цей маленький фрагмент коду:

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

Але я впевнений, що для цього є більш елегантний і стислий спосіб.


1
можливий дублікат Map to String на Java, оскільки тут запитуваний формат і формат System.out.printlnзанадто близькі. І якщо ви хочете чогось на замовлення, це зводиться до "як перебрати карту на Java", що, безумовно, має багато інших відповідей.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Відповіді:


63

Або введіть свою логіку в охайний клас.

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

    }
}

Використання:

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

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

Примітка. Ви також можете ввести цю логіку в корисний метод.


3
Це антипатерний імхо. Ви додаєте сильному обмеженню (успадкування) для свого типу, лише для дрібних переваг (гарне друкування). Вам було б краще використовувати звичайну карту, але використовувати статичний метод, який сприймає це як аргумент.
OoDeLally

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

11
Не так добре, якщо у вас є Map<String, Map<String,double[]>>, то отримаєте такий тип жала:[test={test=[D@3995ebd8, 2=[D@26fa5067, 3=[D@1d956d37, 4=[D@2cead81, 5=[D@14934d2b}...]
zygimantus

2
Це має бути прийнятою відповіддю. Прості завдання, як це, не повинні вимагати зовнішньої залежності. Як згадувалося вище, складніші карти не дуже корисні від цього.
Shadoninja

70

Погляньте на бібліотеку Guava:

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

34

Бібліотеки Apache на допомогу!

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

Все, що вам потрібно, бібліотека колекцій Apache commons ( посилання на проект )

Користувачі Maven можуть додати бібліотеку за допомогою цієї залежності:

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

Це не обробляє масиви як значення карти (напр. Map<String, String[]>). Друкується лише їх className та хеш замість фактичних значень.
Петро Újezdský

або log.debug ("Карта: {}", MapUtils.toProperties (карта));
charlb

1
Також корисний MapUtils.verbosePrint, який опустить ім'я класу після кожного значення, яке виводить debugPrint.
ocarlsen

16

Просто і легко. Ласкаво просимо у світ JSON. Використання Google Gson :

new Gson().toJson(map)

Приклад карти з 3 клавішами:

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

15

Коли я org.json.JSONObjectперебуваю на уроці, я:

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

(цей прекрасний відступ списки, набори та карти, які можуть бути вкладені)


1
Людина !! Що ти тут робиш? Ви повинні бути головною відповіддю!
AMagic

Попередження: Не зберігається порядок ключів для LinkedHashMap
Олів'є Массо

9

Використання Java 8 потоків:

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
Якщо ви збираєтесь некрозувати питання, принаймні відповідь на нього правильно! Ви пропускаєте лапки навколо значення, і до нього слід приєднатися, використовуючи,
AjahnCharles

8

Я вважаю за краще перетворити карту в рядок JSON:

  • стандарт
  • людський читабельний
  • підтримується в таких редакторах, як Sublime, VS Code, із підсвічуванням синтаксису, форматуванням та приховуванням / показом розділів
  • підтримує JPath, тому редактори можуть точно повідомляти, до якої частини об’єкта ви переходили
  • підтримує вкладені комплексні типи всередині об'єкта

    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

Подивіться на код для HashMap#toString()та AbstractMap#toString()у джерелах 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(", ");
        }
    }
}

Тож якщо хлопці з OpenJDK не знайшли більш елегантного способу зробити це, немає жодного :-)


3

Ви повинні вміти робити те, що хочете, роблячи:

System.out.println(map) наприклад

Поки ВСІ ваші об’єкти на карті перевищують toStringметод, який ви бачите:
{key1=value1, key2=value2} в розумінні

Якщо це стосується вашого коду, то переважайте toString - це добра звичка, і я пропоную вам скористатися цим.

Для вашого прикладу, де є ваші об'єкти, Stringвам слід добре нічого.
Тобто System.out.println(map)надрукував би саме те, що потрібно без зайвого коду


1
його ключі та значення - це рядки; Я думаю, що він отримав методом toString покритий tbh.
Том

Ви маєте рацію, але, можливо, він скопіював тривіальний приклад, щоб висловити свою думку. Але я оновлюю відповідь
Cratylus

Так, я мав би вказати, що моя карта - Карта <Рядок, Рядок>. Але я також зазначив, що хочу більше свободи у своїх вхідних значеннях, наприклад, маючи списки, відокремлені комами, у межах значення запису. Тож Map.toString () не зробить.
space_monkey

Інтерфейс java.util.Mapне має договору щодо toString(), тому це залежить від того, Mapщо саме System.out.println(map)-> PrintStream.println(map)-> String.valueOf(map)-> map.toString()спричинить. Буває, що часто використовуваний java.util.AbstractMapзабезпечує гарне представлення рядків для toString(). ... Інші Mapреалізації можуть повернутися до цього Object.toString(), що призводить до не дуже інформативної інформації com.example.MyMap@4e8c0de.
Абдулл

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

Я думаю, що щось подібне було б більш чистим і забезпечить вам більшу гнучкість у вихідному форматі (просто змініть шаблон):

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

Я знаю, що видаляти останню кому некрасиво, але я думаю, що вона чистіша за альтернативи, як, наприклад, у цьому рішенні або вручну за допомогою ітератора.


0

Як швидке і брудне рішення, що використовує існуючу інфраструктуру, ви можете загорнути її uglyPrintedMapв систему java.util.HashMap, а потім використовувати 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

Не відповідає точно на запитання, але варто згадати @ToString анотацію Ломбодок . Якщо ви пояснення @ToStringдо key / valueкласам, то робитиSystem.out.println(map) буде повертати що - щось значуще.

Він також дуже добре працює, наприклад, з картами-картами: Map<MyKeyClass, Map<String, MyValueClass>>буде надруковано як

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


0

String result = objectMapper.writeValueAsString(map) - так просто, як це!

Результат:

{"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 додайте Джексона JSON на ваш однокласник.


0

Оскільки у java 8 є простий спосіб зробити це за допомогою Lambda:

yourMap.keySet().forEach(key -> {
    Object obj = yourMap.get(key);
    System.out.println( obj);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.