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.