【问题标题】:Printing Java Collections Nicely (toString Doesn't Return Pretty Output)很好地打印 Java 集合(toString 不返回漂亮的输出)
【发布时间】:2010-09-28 13:35:53
【问题描述】:

我希望像 Eclipse 调试器一样打印 Stack<Integer> 对象(即 [1,2,3...]),但使用 out = "output:" + stack 打印它不会返回这个好的结果。

澄清一下,我说的是 Java 的内置集合,所以我不能覆盖它的 toString()。

我怎样才能得到一个很好的可打印版本的堆栈?

【问题讨论】:

标签: java debugging pretty-print


【解决方案1】:

较新的 JDK 已经实现了AbstractCollection.toString(),并且Stack 扩展了AbstractCollection,因此您只需在您的集合上调用toString():

    public abstract class AbstractCollection<E> implements Collection<E> {
    ...
    public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";
    
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }

【讨论】:

    【解决方案2】:

    如今,大多数集合在 java (Java7/8) 中都有一个有用的toString()。 所以不需要做流操作来连接你需要的东西,只需覆盖集合中你的值类的toString,你就得到了你需要的东西。

    AbstractMap 和 AbstractCollection 都通过对每个元素调用 toString 来实现 toString()。

    下面是一个显示行为的测试类。

    import java.util.List;
    import java.util.ArrayList;
    import java.util.Map;
    import java.util.HashMap;
    
    public class ToString {
      static class Foo {
        int i;
        public Foo(int i) { this.i=i; }
        @Override
        public String toString() {
            return "{ i: " + i + " }";
        }
      }
      public static void main(String[] args) {
        List<Foo> foo = new ArrayList<>();
        foo.add(new Foo(10));
        foo.add(new Foo(12));
        foo.add(new Foo(13));
        foo.add(new Foo(14));
        System.out.println(foo.toString());
        // prints: [{ i: 10 }, { i: 12 }, { i: 13 }, { i: 14 }]
    
        Map<Integer, Foo> foo2 = new HashMap<>();
        foo2.put(10, new Foo(10));
        foo2.put(12, new Foo(12));
        foo2.put(13, new Foo(13));
        foo2.put(14, new Foo(14));
        System.out.println(foo2.toString());
        // prints: {10={ i: 10 }, 12={ i: 12 }, 13={ i: 13 }, 14={ i: 14 }}
      }
    }
    

    更新 Java 14(2020 年 3 月)

    记录现在是一项预览功能,如果您的班级仅保存数据,则不需要您覆盖 toString()。 Records 实现了一个数据契约,为它的字段提供公共只读访问权限,并为您的方便实现默认函数,如比较、toString 和 hashcode。

    所以一次可以通过以下行为实现Foo:

    import java.util.List;
    import java.util.ArrayList;
    import java.util.Map;
    import java.util.HashMap;
    
    public class ToString {
      static record Foo(int i) { }
      
      public static void main(String[] args) {
        Foo f = new Foo(10);
        System.out.println(f.toString());
        // prints: Foo[i=10]
    
        List<Foo> foo = new ArrayList<>();
        foo.add(new Foo(10));
        foo.add(new Foo(12));
        foo.add(new Foo(13));
        foo.add(new Foo(14));
        System.out.println(foo.toString());
        // prints: [Foo[i=10], Foo[i=12], Foo[i=13], Foo[i=14]]
    
        Map<Integer, Foo> foo2 = new HashMap<>();
        foo2.put(10, new Foo(10));
        foo2.put(12, new Foo(12));
        foo2.put(13, new Foo(13));
        foo2.put(14, new Foo(14));
        System.out.println(foo2.toString());
        // prints: {10=Foo[i=10], 12=Foo[i=12], 13=Foo[i=13], 14=Foo[i=14]}
      }
    }
    

    【讨论】:

      【解决方案3】:

      JSON

      另一种解决方案可能是将您的集合转换为 JSON 格式并打印 Json-String。优点是格式良好且可读的对象字符串,无需实现toString()。

      使用谷歌Gson的示例:

      import com.google.gson.Gson;
      import com.google.gson.GsonBuilder;
      
      ...
      
          printJsonString(stack);
      
      ...
      public static void printJsonString(Object o) {
          GsonBuilder gsonBuilder = new GsonBuilder();
          /*
           * Some options for GsonBuilder like setting dateformat or pretty printing
           */
          Gson gson = gsonBuilder.create();
          String json= gson.toJson(o);
          System.out.println(json);
      }
      

      【讨论】:

        【解决方案4】:

        有两种方法可以简化您的工作。 1.导入Gson库。 2. 使用龙目岛。

        它们都可以帮助您从对象实例创建字符串。 Gson 会解析你的对象,lombok 会覆盖你的类对象的 toString 方法。

        我举了一个关于 Gson prettyPrint 的例子,我创建了帮助类来打印对象和对象集合。如果您使用的是 lombok,您可以将您的类标记为 @ToString 并直接打印您的对象。

        @Scope(value = "prototype")
        @Component
        public class DebugPrint<T> {
           public String PrettyPrint(T obj){
              Gson gson = new GsonBuilder().setPrettyPrinting().create();
              return gson.toJson(obj);
           }
           public String PrettyPrint(Collection<T> list){
              Gson gson = new GsonBuilder().setPrettyPrinting().create();
              return list.stream().map(gson::toJson).collect(Collectors.joining(","));
           }
        

        }

        【讨论】:

          【解决方案5】:

          在 Java8 中

          //will prints each element line by line
          stack.forEach(System.out::println);
          

          或

          //to print with commas
          stack.forEach(
              (ele) -> {
                  System.out.print(ele + ",");
              }
          );
          

          【讨论】:

            【解决方案6】:

            番石榴看起来是个不错的选择:

            Iterables.toString(myIterable)

            【讨论】:

              【解决方案7】:

              使用 Apache Commons 3,您想调用

              StringUtils.join(myCollection, ",")
              

              【讨论】:

                【解决方案8】:

                您可以使用 JAVA 中的 "Objects" 类(从 1.7 开始提供)

                Collection<String> myCollection = Arrays.asList("1273","123","876","897");
                Objects.toString(myCollection);
                

                输出:1273、123、876、897

                另一种可能性是使用来自 Google Guave 的 “MoreObjects”类,它提供了许多有用的帮助函数:

                MoreObjects.toStringHelper(this).add("NameOfYourObject", myCollection).toString());
                

                输出: NameOfYourObject=[1273, 123, 876, 897]

                Guava docs

                【讨论】:

                • Objects.toString() 只是在集合上调用toString()。在您的示例中,这是有效的,因为数组支持的集合上的 toString() 恰好打印得很好。
                【解决方案9】:

                你可以试试

                org.apache.commons.lang3.builder.ToStringBuilder.reflectionToString(yourCollection);
                

                【讨论】:

                  【解决方案10】:
                  String.join(",", yourIterable);
                  

                  (Java 8)

                  【讨论】:

                  • yourIterable 必须是 Iterable
                  • String.join(",", yourCollection.stream().map(o -> o.toString()).collect(Collectors.toList()))
                  • @user1016765 yourCollection.stream().map( o -&gt; o.toString() ).collect( joining(",") )) 更好,因为你从左到右阅读它,你不需要回头看前面来在你的大脑中计算中间列表做了什么
                  【解决方案11】:

                  Apache Commons 项目提供的 MapUtils 类提供了一个 MapUtils.debugPrint 方法,可以漂亮地打印您的地图。

                  【讨论】:

                  【解决方案12】:

                  使用 java 8 流和收集器可以轻松完成:

                  String format(Collection<?> c) {
                    String s = c.stream().map(Object::toString).collect(Collectors.joining(","));
                    return String.format("[%s]", s);
                  }
                  

                  首先我们使用map 和Object::toString 创建Collection&lt;String&gt;,然后使用连接收集器连接集合中的每个项目,并以, 作为分隔符。

                  【讨论】:

                  • 我不得不忍住不要从答案中删除“容易”这个词 ;-) Collections.toString(stack) 会很容易。
                  • 为什么要调用 String.format()?只是为了得到方括号吗?
                  【解决方案13】:

                  应该适用于任何集合,除了Map,但它也很容易支持。 如果需要,修改代码以将这 3 个字符作为参数传递。

                  static <T> String seqToString(Iterable<T> items) {
                      StringBuilder sb = new StringBuilder();
                      sb.append('[');
                      boolean needSeparator = false;
                      for (T x : items) {
                          if (needSeparator)
                              sb.append(' ');
                          sb.append(x.toString());
                          needSeparator = true;
                      }
                      sb.append(']');
                      return sb.toString();
                  }
                  

                  【讨论】:

                    【解决方案14】:

                    在类上实现 toString()。

                    我推荐Apache Commons ToStringBuilder 来简化此操作。有了它,你只需要编写这样的方法:

                    public String toString() {
                         return new ToStringBuilder(this).
                           append("name", name).
                           append("age", age).
                           toString(); 
                    }
                    

                    为了得到这样的输出:

                    Person@7f54[name=Stephen,age=29]

                    还有一个reflective implementation。

                    【讨论】:

                    • ToStringBuilder 通常更适用于携带信息的 bean 和对象,不太适用于复杂的数据结构。如果堆栈对象没有打印所有存储的项目,这将无济于事。
                    • 反射 ToStringBuilder、HashCodeBuilder 和 EqualsBuilder 的使用是非常无效的。虽然输出还可以,但这些课程几乎不是本周的性能高峰......
                    • 问题明确表示该类是内置集合,因此无法修改 toString()。
                    【解决方案15】:

                    在 Collection 上调用 Sop 时要小心,它可能会抛出 ConcurrentModification 异常。因为每个 Collection 的内部 toString 方法在内部调用 Iterator 在 Collection 上。

                    【讨论】:

                      【解决方案16】:

                      只是修改了前面的例子,打印包含用户定义对象的偶数集合。

                      public class ToStringHelper {
                      
                          private  static String separator = "\n";
                      
                          public ToStringHelper(String seperator) {
                              super();
                              ToStringHelper.separator = seperator;
                      
                          }
                      
                          public  static String toString(List<?> l) {
                              StringBuilder sb = new StringBuilder();
                              String sep = "";
                              for (Object object : l) {
                                  String v = ToStringBuilder.reflectionToString(object);
                                  int start = v.indexOf("[");
                                  int end = v.indexOf("]");
                                  String st =  v.substring(start,end+1);
                                  sb.append(sep).append(st);
                                  sep = separator;
                              }
                              return sb.toString();
                          }
                      
                          public static String toString(Map<?,?> m) {
                              StringBuilder sb = new StringBuilder();
                              String sep = "";
                              for (Object object : m.keySet()) {
                                  String v = ToStringBuilder.reflectionToString(m.get(object));
                                  int start = v.indexOf("[");
                                  int end = v.indexOf("]");
                                  String st =  v.substring(start,end+1);
                                  sb.append(sep).append(st);
                                  sep = separator;
                              }
                              return sb.toString();
                          }
                      
                          public static String toString(Set<?> s) {
                              StringBuilder sb = new StringBuilder();
                              String sep = "";
                              for (Object object : s) {
                                  String v = ToStringBuilder.reflectionToString(object);
                                  int start = v.indexOf("[");
                                  int end = v.indexOf("]");
                                  String st =  v.substring(start,end+1);
                                  sb.append(sep).append(st);
                                  sep = separator;
                              }
                              return sb.toString();
                          }
                      
                          public static void print(List<?> l) {
                              System.out.println(toString(l));    
                          }
                          public static void print(Map<?,?> m) {
                              System.out.println(toString(m));    
                          }
                          public static void print(Set<?> s) {
                              System.out.println(toString(s));    
                          }
                      
                      }
                      

                      【讨论】:

                        【解决方案17】:

                        System.out.println(Collection c) 已经以可读格式打印任何类型的集合。只有当集合中包含用户定义的对象时,才需要在用户定义的类中实现 toString() 来显示内容。

                        【讨论】:

                          【解决方案18】:

                          我同意上述 cmets 关于在您自己的类上覆盖 toString()(以及尽可能自动化该过程)。

                          对于您没有定义的类,您可以编写一个 ToStringHelper 类,并为每个您想要根据自己的喜好处理的库类添加一个重载方法:

                          public class ToStringHelper {
                              //... instance configuration here (e.g. punctuation, etc.)
                              public toString(List m) {
                                  // presentation of List content to your liking
                              }
                              public toString(Map m) {
                                  // presentation of Map content to your liking
                              }
                              public toString(Set m) {
                                  // presentation of Set content to your liking
                              }
                              //... etc.
                          }
                          

                          编辑:回应 xukxpvfzflbbld 的评论,这里是前面提到的案例的可能实现。

                          package com.so.demos;
                          
                          import java.util.List;
                          import java.util.Map;
                          import java.util.Set;
                          
                          public class ToStringHelper {
                          
                              private String separator;
                              private String arrow;
                          
                              public ToStringHelper(String separator, String arrow) {
                                  this.separator = separator;
                                  this.arrow = arrow;
                              }
                          
                             public String toString(List<?> l) {
                                  StringBuilder sb = new StringBuilder("(");
                                  String sep = "";
                                  for (Object object : l) {
                                      sb.append(sep).append(object.toString());
                                      sep = separator;
                                  }
                                  return sb.append(")").toString();
                              }
                          
                              public String toString(Map<?,?> m) {
                                  StringBuilder sb = new StringBuilder("[");
                                  String sep = "";
                                  for (Object object : m.keySet()) {
                                      sb.append(sep)
                                        .append(object.toString())
                                        .append(arrow)
                                        .append(m.get(object).toString());
                                      sep = separator;
                                  }
                                  return sb.append("]").toString();
                              }
                          
                              public String toString(Set<?> s) {
                                  StringBuilder sb = new StringBuilder("{");
                                  String sep = "";
                                  for (Object object : s) {
                                      sb.append(sep).append(object.toString());
                                      sep = separator;
                                  }
                                  return sb.append("}").toString();
                              }
                          
                          }
                          

                          这不是一个成熟的实现,而只是一个入门。

                          【讨论】:

                            【解决方案19】:

                            如果这是你自己的集合类而不是内置的,你需要重写它的 toString 方法。 Eclipse 会为没有硬连线格式的任何对象调用该函数。

                            【讨论】:

                            • 那么 Eclipse 如何使用硬连线格式来格式化这些类?这就是我要找的。​​span>
                            【解决方案20】:

                            您可以将其转换为数组,然后使用Arrays.toString(Object[]) 打印出来:

                            System.out.println(Arrays.toString(stack.toArray()));
                            

                            【讨论】:

                            • 我喜欢。简单,干净。老实说,Collections 也需要一个 toString 方法,但这也有效。
                            • @Tovi7 这可能不是因为大多数 OOTB 集合已经提供了可读的 toString(),而数组没有。
                            • @Boosha 将堆栈转换为字符串也需要 O(n) 时间,将字符串打印到控制台也需要 O(n) 时间
                            • stack.toArray() 在 CPU、时间和内存方面可能非常昂贵。迭代原始集合/可迭代的解决方案可能会减少资源消耗。
                            猜你喜欢
                            • 2019-09-08
                            • 2016-08-26
                            • 1970-01-01
                            • 1970-01-01
                            • 2013-02-16
                            • 1970-01-01
                            • 2021-12-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多