Jolie-imprimer une carte en Java

136

Je recherche une manière agréable d'imprimer un fichier Map.

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

Je veux plus de liberté dans mes valeurs d'entrée de carte et je recherche quelque chose de plus comme ceci: key1="value1", key2="value2", key3="value3"

J'ai écrit ce petit bout de code:

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

Mais je suis sûr qu'il existe une manière plus élégante et concise de le faire.

space_monkey
la source
1
duplication possible de Map to String en Java car le format demandé ici et celui de System.out.printlnsont trop proches. Et si vous voulez quelque chose de personnalisé, cela se résume à "comment itérer sur une carte en Java" qui a certainement beaucoup d'autres réponses.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

63

Ou mettez votre logique dans une petite classe bien rangée.

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

    }
}

Usage:

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

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

Remarque: vous pouvez également mettre cette logique dans une méthode utilitaire.

Adarshr
la source
3
C'est un anti-modèle à mon humble avis. Vous ajoutez une forte contrainte (héritage) à votre type, juste pour de petits avantages (jolie impression). Vous feriez mieux d'utiliser une carte régulière, mais utilisez une méthode statique qui la prend comme argument.
OoDeLally
304
Arrays.toString(map.entrySet().toArray())
Arkadiusz Cieśliński
la source
11
Pas si bon si vous en avez Map<String, Map<String,double[]>>, alors vous aurez ce type de piqûre:[test={test=[D@3995ebd8, 2=[D@26fa5067, 3=[D@1d956d37, 4=[D@2cead81, 5=[D@14934d2b}...]
zygimantus
2
Cela devrait être la réponse acceptée. Des tâches simples comme celle-ci ne devraient pas nécessiter de dépendance externe. Comme mentionné ci-dessus, les cartes plus complexes n'en bénéficient pas aussi facilement.
Shadoninja
70

Jetez un œil à la bibliothèque Guava:

Joiner.MapJoiner mapJoiner = Joiner.on(",").withKeyValueSeparator("=");
System.out.println(mapJoiner.join(map));
Mark Wigmans
la source
34

Les bibliothèques Apache à la rescousse!

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

Tout ce dont vous avez besoin bibliothèque Apache commons-collections ( lien du projet )

Les utilisateurs Maven peuvent ajouter la bibliothèque en utilisant cette dépendance:

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>
tbraun
la source
Cela ne gère pas les tableaux en tant que valeurs de la carte (par exemple Map<String, String[]>). Seuls leur nom de classe et leur hachage sont imprimés à la place des valeurs réelles.
Petr Újezdský
ou log.debug ("Map: {}", MapUtils.toProperties (map));
charlb
1
MapUtils.verbosePrint est également utile, qui omettra le nom de la classe après chaque valeur générée par debugPrint.
ocarlsen
16

Simple et facile. Bienvenue dans le monde JSON. Utilisation du Gson de Google :

new Gson().toJson(map)

Exemple de carte avec 3 touches:

{"array":[null,"Some string"],"just string":"Yo","number":999}
AlikElzin-kilaka
la source
15

Quand j'ai org.json.JSONObjectdans le classpath, je fais:

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

(cela indente magnifiquement les listes, les ensembles et les cartes qui peuvent être imbriqués)

Thamme Gowda
la source
1
Homme!! Qu'est ce que tu fais en bas? Vous devriez être la meilleure réponse!
AMagic
Attention: Ne conserve pas l'ordre des clés pour un LinkedHashMap
Olivier Masseau
9

Utilisation de Java 8 Streams:

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
la source
2
Si vous voulez nécroser une question, répondez-y au moins correctement! Il vous manque les guillemets autour de la valeur et il devrait être joint en utilisant,
AjahnCharles
8

Je préfère convertir la carte en une chaîne JSON c'est:

  • une référence
  • lisible par l'homme
  • pris en charge dans les éditeurs tels que Sublime, VS Code, avec mise en évidence de la syntaxe, mise en forme et masquage / affichage des sections
  • prend en charge JPath afin que les éditeurs puissent indiquer exactement la partie de l'objet vers laquelle vous avez navigué
  • prend en charge les types complexes imbriqués dans l'objet

    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
la source
4

Regardez le code pour HashMap#toString()et AbstractMap#toString()dans les sources 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(", ");
        }
    }
}

Donc, si les gars d'OpenJDK n'ont pas trouvé de moyen plus élégant de le faire, il n'y en a pas :-)

parasietje
la source
3

Vous devriez pouvoir faire ce que vous voulez en faisant:

System.out.println(map) par exemple

Tant que TOUS vos objets de la carte ont overiden la toStringméthode que vous verriez :
{key1=value1, key2=value2}d'une manière significative

Si c'est pour votre code, alors le dépassement toStringest une bonne habitude et je vous suggère de plutôt opter pour cela.

Pour votre exemple où sont vos objets, Stringvous devriez aller bien sans rien d'autre.
Ie System.out.println(map)imprimerait exactement ce dont vous avez besoin sans aucun code supplémentaire

Cratyle
la source
1
ses clés et ses valeurs sont des chaînes; Je pense qu'il a couvert la méthode toString tbh.
Tom
Vous avez raison, mais peut-être qu'il a copié un exemple trivial pour faire valoir son point.Mais je mets à jour la réponse
Cratylus
Oui, j'aurais dû indiquer que ma carte est Map <String, String>. Mais j'ai également indiqué que je voulais plus de liberté dans mes valeurs d'entrée, par exemple avoir des listes séparées par des virgules dans la valeur d'une entrée. Donc Map.toString () ne fera pas l'affaire.
space_monkey
L'interface java.util.Mapn'a pas de contrat concernant toString(), donc c'est à la Mapmise en œuvre réelle ce que System.out.println(map)-> PrintStream.println(map)-> String.valueOf(map)-> map.toString()provoquera. Il arrive que le souvent utilisé java.util.AbstractMapfournit une belle représentation sous forme de chaîne pour toString(). ... D'autres Mapimplémentations peuvent se rabattre sur Object.toString(), ce qui se traduit par le moins informatif com.example.MyMap@4e8c0de.
Abdull du
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);
}
pierres333
la source
0

Je suppose que quelque chose comme celui-ci serait plus propre et vous offrirait plus de flexibilité avec le format de sortie (changez simplement le modèle):

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

Je sais que devoir supprimer la dernière virgule est moche, mais je pense que c'est plus propre que des alternatives comme celle de cette solution ou manuellement en utilisant un itérateur.

Messieurs
la source
0

En tant que solution rapide et sale tirant parti de l'infrastructure existante, vous pouvez envelopper votre uglyPrintedMapdans un java.util.HashMap, puis l'utiliser 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
la source
0

Ne répond pas exactement à la question, mais il convient de mentionner l' @ToString annotation Lombodok . Si vous annotez avec @ToStringles key / valueclasses, alors faire System.out.println(map)retournera quelque chose de significatif.

Cela fonctionne également très bien avec les cartes-de-cartes, par exemple: Map<MyKeyClass, Map<String, MyValueClass>>sera imprimé comme

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

user1485864
la source
0

String result = objectMapper.writeValueAsString(map) - aussi simple que ça!

Résultat:

{"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 ajoutez Jackson JSON à votre chemin de classe.

user2171669
la source
0

Depuis java 8, il existe un moyen simple de le faire avec Lambda:

yourMap.keySet().forEach(key -> {
    Object obj = yourMap.get(key);
    System.out.println( obj);
}
Tomasz Stępnik
la source