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ć.

space_monkey
źródło
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.

adarshr
źródło
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())
Arkadiusz Cieśliński
źródło
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));
Mark Wigmans
źródło
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>
tbraun
źródło
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}
AlikElzin-kilaka
źródło
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)

Thamme Gowda
źródło
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);
Nikita Koksharov
źródło
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 "";
    }
MattG
źródło
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 :-)

parasietje
źródło
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

Cratylus
źródło
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);
}
kamienie333
źródło
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.

Szanowni Państwo
źródło
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
Abdull
źródło
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...),..}, ... }

user1485864
źródło
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.

user2171669
źródło
0

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

yourMap.keySet().forEach(key -> {
    Object obj = yourMap.get(key);
    System.out.println( obj);
}
Tomasz Stępnik
źródło