Pretty-распечатать карту в 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», что, безусловно, имеет много других ответов.
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Ответы:


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
Это должен быть принятый ответ. Простые задачи, подобные этой, не должны требовать внешней зависимости. Как упомянуто выше, более сложные карты не так легко выигрывают от этого.
Шадониня,

70

Взгляните на библиотеку Guava:

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

34

Апачские библиотеки на помощь!

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

Все, что вам нужно, библиотека Apache commons-collection ( ссылка на проект )

Пользователи Maven могут добавить библиотеку, используя эту зависимость:

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

Это не обрабатывает массивы как значения карты (например, Map<String, String[]>). Только их className и hash печатаются вместо фактических значений.
Петр Рождский

или log.debug ("Map: {}", MapUtils.toProperties (map));
charlb

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

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.
Том

Вы правы, но, возможно, он скопировал тривиальный пример, чтобы высказать свою точку зрения. Но я обновляю ответ
Cratylus

Да, я должен был указать, что моя карта - это карта <String, String>. Но я также указал, что я хочу больше свободы в моих значениях записи, например, иметь разделенные запятыми списки в значении записи. Так что 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 аннотацию Lombodok . Если вы пояснение @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, есть простой способ сделать это с помощью лямбды:

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