พริตตี้พิมพ์แผนที่ใน Java


136

ฉันกำลังมองหาวิธีที่ดีในการพิมพ์สวย ๆ Map

map.toString() ให้ฉัน: {key1=value1, key2=value2, key3=value3}

ฉันต้องการอิสระมากขึ้นในค่ารายการแผนที่ของฉันและฉันกำลังมองหาบางอย่างเช่นนี้: key1="value1", key2="value2", key3="value3"

ฉันเขียนโค้ดเล็ก ๆ นี้:

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

แต่ฉันแน่ใจว่ามีวิธีที่สง่างามและรัดกุมกว่านี้


1
เป็นไปได้ที่ซ้ำกันของMap to String ใน Javaเนื่องจากรูปแบบที่ร้องขอที่นี่และที่System.out.printlnใกล้เกินไป และถ้าคุณต้องการสิ่งที่กำหนดเองสิ่งนี้จะลดลงไปที่ "วิธีการย้ำผ่านแผนที่ใน Java" ซึ่งมีคำตอบอื่น ๆ อีกมากมาย
Ciro Santilli 法轮功冠状病六四事件法轮功

คำตอบ:


63

หรือวางตรรกะของคุณลงในชั้นเรียนเล็ก ๆ ที่เป็นระเบียบ

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

    }
}

การใช้งาน:

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

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

หมายเหตุ: คุณสามารถใส่ตรรกะนั้นลงในวิธีการยูทิลิตี้


3
นี่คือ imipattern antipattern คุณเพิ่มข้อ จำกัด ที่แข็งแกร่ง (การสืบทอด) ให้กับชนิดของคุณเพียงเพื่อผลประโยชน์เล็กน้อย (การพิมพ์ที่สวย) คุณน่าจะใช้แผนที่ปกติดีกว่า แต่ใช้วิธีสแตติกที่รับเป็นอาร์กิวเมนต์
OoDeLally

304
Arrays.toString(map.entrySet().toArray())

11
ดังนั้นไม่ดีถ้าคุณมีMap<String, Map<String,double[]>>แล้วคุณจะได้รับชนิดนี้ต่อย:[test={test=[D@3995ebd8, 2=[D@26fa5067, 3=[D@1d956d37, 4=[D@2cead81, 5=[D@14934d2b}...]
zygimantus

2
นี่ควรเป็นคำตอบที่ยอมรับได้ งานง่าย ๆ เช่นนี้ไม่จำเป็นต้องพึ่งพาภายนอก ดังกล่าวข้างต้นแผนที่ที่ซับซ้อนมากขึ้นไม่ได้ประโยชน์จากเรื่องนี้
Shadoninja


34

ห้องสมุด Apache เพื่อช่วยเหลือ!

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

สิ่งที่คุณต้องมีห้องสมุด Apache Commons-collection ( ลิงค์โครงการ )

ผู้ใช้ Maven สามารถเพิ่มห้องสมุดโดยใช้การขึ้นต่อกันนี้:

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>

สิ่งนี้ไม่ได้จัดการอาร์เรย์เป็นค่าของแผนที่ (เช่นMap<String, String[]>) พิมพ์ className และ hash ของพวกเขาเท่านั้นแทนที่จะพิมพ์ค่าจริง
Petr Újezdský

หรือ log.debug ("แผนที่: {}", MapUtils.toProperties (แผนที่));
charlb

1
มีประโยชน์อีกอย่างคือ MapUtils.verbosePrint ซึ่งจะละเว้นชื่อคลาสหลังจากแต่ละค่าที่เอาต์พุต debugPrint
ocarlsen


15

เมื่อฉันorg.json.JSONObjectอยู่ใน classpath ฉันจะ:

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

(รายการย่อหน้าชุดและแผนที่ที่สวยงามซึ่งอาจซ้อนอยู่)


1
ชาย!! คุณทำอะไรลงที่นี่ คุณควรเป็นคำตอบที่ดีที่สุด!
AMagic

คำเตือน: ไม่รักษาลำดับของคีย์สำหรับ LinkedHashMap
Olivier Masseau

9

ใช้ 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
หากคุณกำลังจะตอบคำถามอย่างน้อยตอบคำถามให้ถูกต้อง! คุณไม่มีเครื่องหมายคำพูดรอบค่าและควรเข้าร่วมโดยใช้,
AjahnCharles

8

ฉันชอบที่จะแปลงแผนที่เป็นสตริง JSON มันคือ:

  • มาตรฐาน
  • มนุษย์อ่านได้
  • ได้รับการสนับสนุนในเครื่องมือแก้ไขเช่น Sublime, VS Code พร้อมการเน้นไวยากรณ์การจัดรูปแบบและส่วนซ่อน / แสดง
  • สนับสนุน JPath เพื่อให้ผู้แก้ไขสามารถรายงานได้อย่างแม่นยำว่าส่วนใดของวัตถุที่คุณสำรวจ
  • รองรับประเภทที่ซับซ้อนซ้อนกันภายในวัตถุ

    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

ดูรหัสHashMap#toString()และAbstractMap#toString()ในแหล่ง 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(", ");
        }
    }
}

ดังนั้นถ้าคนจาก OpenJDK ไม่ได้หาวิธีที่สง่างามกว่านี้ก็ไม่มี :-)


3

คุณควรจะทำสิ่งที่คุณต้องการโดยทำ:

System.out.println(map) ตัวอย่างเช่น

ตราบใดที่วัตถุทั้งหมดของคุณในแผนที่มีtoStringวิธีที่เกินกว่าที่คุณจะเห็น:
{key1=value1, key2=value2}ในลักษณะที่มีความหมายเต็ม

หากนี่เป็นรหัสของคุณการเอาชนะtoStringเป็นนิสัยที่ดีและฉันขอแนะนำให้คุณลองทำเช่นนั้นแทน

สำหรับตัวอย่างของคุณที่วัตถุStringของคุณคุณควรจะดีโดยไม่มีอะไรอื่น
เช่นSystem.out.println(map)จะพิมพ์สิ่งที่คุณต้องการโดยไม่ต้องมีรหัสพิเศษใด ๆ


1
กุญแจและค่านิยมของเขาคือสตริง ฉันคิดว่าเขาได้รับวิธี toString ครอบคลุม tbh
ทอม

คุณพูดถูก แต่บางทีเขาก็คัดลอกตัวอย่างเล็ก ๆ น้อย ๆ เพื่อให้ประเด็นของเขา แต่ฉันอัปเดตคำตอบ
Cratylus

ใช่ฉันควรระบุว่าแผนที่ของฉันคือ Map <String, String> แต่ฉันยังระบุด้วยว่าฉันต้องการอิสระมากขึ้นในค่ารายการของฉันเช่นมีรายการคั่นด้วยเครื่องหมายจุลภาคภายในค่าของรายการ ดังนั้น Map.toString () จะไม่ทำ
space_monkey

อินเทอร์เฟซjava.util.Mapไม่มีสัญญาใดtoString()ๆ ดังนั้นจึงขึ้นอยู่กับการMapใช้งานจริงสิ่งที่System.out.println(map)-> PrintStream.println(map)-> String.valueOf(map)-> map.toString()จะทำให้เกิด มันเกิดขึ้นที่มักมือสองให้การแสดงสตริงที่ดีสำหรับjava.util.AbstractMap toString()... อื่น ๆMapการใช้งานอาจจะถอยกลับไปที่ผลลัพธ์ในไม่ให้ข้อมูลObject.toString() com.example.MyMap@4e8c0de
อับดุล

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

ฉันเดาว่าสิ่งนี้จะสะอาดกว่าและให้ความยืดหยุ่นกับรูปแบบผลลัพธ์มากขึ้น (เพียงเปลี่ยนเทมเพลต):

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

ฉันรู้ว่าต้องลบเครื่องหมายจุลภาคสุดท้ายน่าเกลียด แต่ฉันคิดว่ามันสะอาดกว่าทางเลือกอื่น ๆ เช่นหนึ่งในโซลูชันนี้หรือด้วยตนเองโดยใช้ตัววนซ้ำ


0

ในฐานะที่เป็นประโยชน์วิธีที่รวดเร็วและสกปรกโครงสร้างพื้นฐานที่มีอยู่คุณสามารถห่อของคุณuglyPrintedMapเป็นแล้วการใช้งาน java.util.HashMaptoString()

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

ไม่ได้คำตอบตรงคำถาม แต่มันเป็นมูลค่าการกล่าวขวัญ Lombodok คำอธิบายประกอบ@ToString หากคุณใส่คำอธิบายประกอบกับเรียนแล้วทำ@ToStringkey / valueSystem.out.println(map)จะกลับมามีความหมาย

นอกจากนี้ยังใช้งานได้ดีกับ maps-of-maps เช่น: Map<MyKeyClass, Map<String, MyValueClass>>จะถูกพิมพ์เป็น

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


0

String result = objectMapper.writeValueAsString(map) - ง่ายอย่างนี้!

ผลลัพธ์:

{"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 เพิ่ม Jackson JSON ให้กับ classpath ของคุณ


0

ตั้งแต่ java 8 มีวิธีง่าย ๆ ในการทำกับ Lambda:

yourMap.keySet().forEach(key -> {
    Object obj = yourMap.get(key);
    System.out.println( obj);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.