Wydrukuj mapę w języku Java


136

Szukam ładnego sposobu na ładne wydrukowanie pliku Map.

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

Chcę większej swobody w wartościach wpisu mapy i szukam czegoś bardziej podobnego: key1="value1", key2="value2", key3="value3"

Napisałem ten mały fragment kodu:

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

Ale jestem pewien, że istnieje bardziej elegancki i zwięzły sposób, aby to zrobić.


1
możliwy duplikat Map to String w Javie, ponieważ format żądany tutaj i format System.out.printlnsą zbyt zbliżone. A jeśli chcesz czegoś niestandardowego, sprowadza się to do „jak iterować mapę w Javie”, co z pewnością ma wiele innych odpowiedzi.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:


63

Lub umieść swoją logikę w uporządkowanej małej klasie.

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

    }
}

Stosowanie:

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

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

Uwaga: Możesz również umieścić tę logikę w metodzie narzędziowej.


3
To jest imho anty-wzór. Dodajesz silne ograniczenie (dziedziczenie) do swojego typu, tylko dla drobnych korzyści (ładne drukowanie). Lepiej byłoby użyć zwykłej mapy, ale użyj metody statycznej, która przyjmuje ją jako argument.
OoDeLally

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

11
Nie tak dobrze, jeśli masz Map<String, Map<String,double[]>>, to dostaniesz tego typu żądło:[test={test=[D@3995ebd8, 2=[D@26fa5067, 3=[D@1d956d37, 4=[D@2cead81, 5=[D@14934d2b}...]
zygimantus

2
To powinna być akceptowana odpowiedź. Takie proste zadania nie powinny wymagać zależności zewnętrznej. Jak wspomniano powyżej, bardziej złożone mapy nie korzystają z tego tak łatwo.
Shadoninja,

70

Zajrzyj do biblioteki Guava:

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

34

Biblioteki Apache na ratunek!

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

Wszystko, czego potrzebujesz biblioteka Apache commons-collections ( link do projektu )

Użytkownicy Mavena mogą dodać bibliotekę przy użyciu tej zależności:

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

To nie obsługuje tablic jako wartości mapy (np. Map<String, String[]>). Zamiast rzeczywistych wartości drukowane są tylko ich nazwa klasy i skrót.
Petr Újezdský

lub log.debug ("Mapa: {}", MapUtils.toProperties (mapa));
charlb

1
Przydatny jest również MapUtils.verbosePrint, który pominie nazwę klasy po każdej wartości wyjściowej debugPrint.
ocarlsen

16

Proste i łatwe. Witamy w świecie JSON. Korzystanie z Google's Gson :

new Gson().toJson(map)

Przykład mapy z 3 klawiszami:

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

15

Kiedy mam org.json.JSONObjectw ścieżce klas, robię:

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

(to pięknie wcina listy, zbiory i mapy, które mogą być zagnieżdżane)


1
Człowiek!! Co tu robisz? Powinieneś być najlepszą odpowiedzią!
AMagic

Ostrzeżenie: nie zachowuje kolejności kluczy dla LinkedHashMap
Olivier Masseau

9

Korzystanie ze strumieni 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
Jeśli masz zamiar nekroć pytanie, przynajmniej odpowiedz na nie poprawnie! Brakuje cudzysłowów wokół wartości i należy ją połączyć za pomocą,
AjahnCharles

8

Wolę przekonwertować mapę na ciąg JSON, jest to:

  • standardowy
  • czytelny dla człowieka
  • obsługiwane w edytorach, takich jak Sublime, VS Code, z podświetlaniem składni, formatowaniem i ukrywaniem / pokazywaniem sekcji
  • obsługuje JPath, dzięki czemu redaktorzy mogą dokładnie raportować, do której części obiektu dotarłeś
  • obsługuje zagnieżdżone typy złożone w obiekcie

    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

Spójrz na kod dla HashMap#toString()i AbstractMap#toString()w źródłach 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(", ");
        }
    }
}

Jeśli więc chłopaki z OpenJDK nie znaleźli na to bardziej eleganckiego sposobu, to nie ma :-)


3

Powinieneś być w stanie robić, co chcesz, robiąc:

System.out.println(map) na przykład

Tak długo, jak WSZYSTKIE twoje obiekty na mapie przesłoniły toStringmetodę, którą zobaczysz:
{key1=value1, key2=value2}w znaczący sposób

Jeśli to jest dla twojego kodu, to przesadzanie toStringjest dobrym nawykiem i sugeruję, abyś zamiast tego wybrał to.

Na przykład, gdzie twoje obiekty są Strings, powinno być dobrze bez niczego innego.
Czyli System.out.println(map)będzie drukować dokładnie to, czego potrzebujesz, bez żadnego dodatkowego kodu


1
jego klucze i wartości to łańcuchy; Myślę, że omówił metodę toString tbh.
Tom

Masz rację, ale być może skopiował trywialny przykład, aby przedstawić swój punkt widzenia, ale aktualizuję odpowiedź
Cratylus

Tak, powinienem był wskazać, że moja mapa to Map <String, String>. Ale wskazałem również, że chcę większej swobody w moich wartościach wejściowych, na przykład mając listy oddzielone przecinkami w wartości wpisu. Więc Map.toString () nie będzie działać.
space_monkey

Interfejs java.util.Mapnie jest objęty żadną umową toString(), więc od rzeczywistej Mapimplementacji zależy, co System.out.println(map)-> PrintStream.println(map)-> String.valueOf(map)-> map.toString()spowoduje. Zdarza się, że często używany java.util.AbstractMapzapewnia ładną reprezentację ciągu toString(). ... Inne Mapimplementacje mogą wrócić do Object.toString(), co skutkuje brakiem informacji com.example.MyMap@4e8c0de.
Abdull

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

Wydaje mi się, że coś takiego byłoby bardziej przejrzyste i zapewniłoby większą elastyczność dzięki formatowi wyjściowemu (po prostu zmień szablon):

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

Wiem, że usunięcie ostatniego przecinka jest brzydkie, ale myślę, że jest to czystsze niż alternatywy, takie jak ta w tym rozwiązaniu lub ręcznie za pomocą iteratora.


0

Jako szybkie i brudne rozwiązanie wykorzystujące istniejącą infrastrukturę, możesz opakować je uglyPrintedMapw plik java.util.HashMap, a następnie użyć 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

Nie odpowiada dokładnie na pytanie, ale warto wspomnieć o @ToString adnotacji Lombodok . Jeśli dodasz adnotacje @ToStringdo key / valueklas, działanie System.out.println(map)zwróci coś znaczącego.

Działa również bardzo dobrze z mapami map, na przykład: Map<MyKeyClass, Map<String, MyValueClass>>zostaną wydrukowane jako

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


0

String result = objectMapper.writeValueAsString(map) - takie proste!

Wynik:

{"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 dodaj Jackson JSON do swojej ścieżki klas.


0

Od Java 8 można to łatwo zrobić z Lambda:

yourMap.keySet().forEach(key -> {
    Object obj = yourMap.get(key);
    System.out.println( obj);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.