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

Но я уверен, что есть более элегантный и лаконичный способ сделать это.

space_monkey
источник
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));

Примечание. Вы также можете поместить эту логику в служебный метод.

adarshr
источник
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>
tbraun
источник
Это не обрабатывает массивы как значения карты (например, 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}
AlikElzin-kilaka
источник
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 "";
    }
MattG
источник
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 не нашли более элегантный способ сделать это, то их нет :-)

parasietje
источник
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);
}
stones333
источник
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();

Я знаю, что убирать последнюю запятую - это ужасно, но я думаю, что она чище, чем альтернативы, подобные той, что в этом решении, или ручное использование итератора.

Sirs
источник
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
Abdull
источник
0

Точно не отвечает на вопрос, но стоит упомянуть @ToString аннотацию Lombodok . Если вы пояснение @ToStringк key / valueклассам, то делатьSystem.out.println(map) будет возвращать что - то значимое.

Он также очень хорошо работает с картами карт, например: Map<MyKeyClass, Map<String, MyValueClass>>будет напечатан как

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

user1485864
источник
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 в ваш путь к классам.

user2171669
источник
0

Начиная с Java 8, есть простой способ сделать это с помощью лямбды:

yourMap.keySet().forEach(key -> {
    Object obj = yourMap.get(key);
    System.out.println( obj);
}
Томаш Стемпник
источник