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.


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.


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

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

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>

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}

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)


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

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 "";
    }

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 :-)


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


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

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.


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

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...),..}, ... }


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.


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);
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.