Je cherche un moyen agréable d'imprimer joliment un Map
.
map.toString()
me donne: {key1=value1, key2=value2, key3=value3}
Je veux plus de liberté dans les valeurs d'entrée de ma carte et je cherche quelque chose qui ressemble à ceci: key1="value1", key2="value2", key3="value3"
J'ai écrit ce petit morceau 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 un moyen plus élégant et concis de procéder.
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.
Arrays.toString(map.entrySet().toArray())
Jetez un coup d'œil à la bibliothèque Guava:
Joiner.MapJoiner mapJoiner = Joiner.on(",").withKeyValueSeparator("=");
System.out.println(mapJoiner.join(map));
Les bibliothèques Apache à la rescousse!
MapUtils.debugPrint(System.out, "myMap", map);
Tout ce dont vous avez besoin Apache commons-collections library ( lien de 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>
Simple et facile. Bienvenue dans le monde JSON. Utilisation de Google Gson :
new Gson().toJson(map)
Exemple de carte avec 3 clés:
{"array":[null,"Some string"],"just string":"Yo","number":999}
Quand j'ai org.json.JSONObject
dans le classpath, je fais:
Map<String, Object> stats = ...;
System.out.println(new JSONObject(stats).toString(2));
(cela met magnifiquement en retrait des listes, des ensembles et des cartes qui peuvent être imbriquées)
Je préfère convertir la carte en une chaîne JSON:
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 "";
}
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);
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 :-)
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);
}
Vous devriez pouvoir faire ce que vous voulez en faisant:
System.out.println(map)
par exemple
Tant que TOUS vos objets sur la carte ont surchargé la méthode toString
, vous verrez:{key1=value1, key2=value2}
d'une manière significative
Si ceci est pour votre code, alors toString
est une bonne habitude et je vous suggère d’y aller plutôt.
Pour votre exemple où vos objets sont String
s vous devriez aller sans rien d'autre.
C'est à dire. System.out.println(map)
imprimerait exactement ce dont vous avez besoin sans code supplémentaire
Je suppose que quelque chose comme ceci serait plus propre et vous donnerait plus de flexibilité avec le format de sortie (changez simplement de 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 dans cette solution ou manuellement à l'aide d'un itérateur.
En tant que solution rapide et optimisée exploitant l'infrastructure existante, vous pouvez envelopper votre uglyPrintedMap
dans un Java.util.HashMap
, puis 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
String result = objectMapper.writeValueAsString(map)
- aussi simple que cela!
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 ...}
P.S. Ajoutez Jackson JSON à votre chemin de classe.
Ne répond pas exactement à la question, mais il convient de mentionner Lombodok @ToString
annotation . Si vous annotez avec @ToString
les classes key / value
, faire System.out.println(map)
retournera quelque chose de significatif.
Cela fonctionne aussi 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...),..},
...
}