【问题标题】:Lining up values in pretty print table view java在漂亮的打印表视图java中排列值
【发布时间】:2021-02-10 21:15:11
【问题描述】:

在以“漂亮的表格视图”方式打印出来后,是否有一种万无一失或简单的方法来排列值?

我的测试数据集代码是:

Map<Integer, List<Boolean>> map = new HashMap<>();
        Random r = new Random(System.currentTimeMillis());

        map.put(1000 + r.nextInt(50000), Arrays.asList(true, false, false));
        map.put(100 + r.nextInt(50000), Arrays.asList(false, false, false));
        map.put(1000 + r.nextInt(50000), Arrays.asList(true, true, false));
        map.put(100 + r.nextInt(50000), Arrays.asList(true, false, true));
        map.put(1000 + r.nextInt(50000), Arrays.asList(false, true, true));
        map.put(1000 + r.nextInt(50000), Arrays.asList(true, false, false));
        map.put(1000 + r.nextInt(50000), Arrays.asList(false, false, false));
        map.put(10000 + r.nextInt(50000), Arrays.asList(true, true, false));
        map.put(1000 + r.nextInt(50000), Arrays.asList(true, false, true));
        map.put(1000 + r.nextInt(5000), Arrays.asList(true, false, false));
        map.put(100 + r.nextInt(50000), Arrays.asList(false, false, false));
        map.put(10000 + r.nextInt(50000), Arrays.asList(true, true, false));
        map.put(1000 + r.nextInt(5000), Arrays.asList(true, false, true));
        map.put(100 + r.nextInt(50000), Arrays.asList(false, true, true));
        map.put(100 + r.nextInt(5000), Arrays.asList(true, false, false));
        map.put(1000 + r.nextInt(50000), Arrays.asList(false, false, false));
        map.put(1000 + r.nextInt(5000), Arrays.asList(true, true, false));
        map.put(10000 + r.nextInt(50000), Arrays.asList(true, false, true));
        map.put(1000 + r.nextInt(50000), Arrays.asList(true, false, false));
        map.put(10000 + r.nextInt(50000), Arrays.asList(false, false, false));
        map.put(1000 + r.nextInt(50000), Arrays.asList(true, true, false));
        map.put(10000 + r.nextInt(50000), Arrays.asList(true, false, true));
        map.put(1000 + r.nextInt(5000), Arrays.asList(false, true, true));
        map.put(1000 + r.nextInt(50000), Arrays.asList(true, false, false));
        map.put(10000 + r.nextInt(50000), Arrays.asList(false, false, false));
        map.put(10000 + r.nextInt(50000), Arrays.asList(true, true, false));
        map.put(1000 + r.nextInt(50000), Arrays.asList(true, false, true));
        map.put(10000 + r.nextInt(50000), Arrays.asList(true, false, false));
        map.put(10000 + r.nextInt(50000), Arrays.asList(false, false, false));
        map.put(1000 + r.nextInt(50000), Arrays.asList(true, true, false));
        map.put(10000 + r.nextInt(50000), Arrays.asList(true, false, true));
        map.put(100 + r.nextInt(50000), Arrays.asList(false, true, true));
        map.put(1000 + r.nextInt(50000), Arrays.asList(true, false, false));
        map.put(10000 + r.nextInt(50000), Arrays.asList(false, false, false));
        map.put(10000 + r.nextInt(50000), Arrays.asList(true, true, false));
        map.put(100 + r.nextInt(50000), Arrays.asList(true, false, true));
        map.put(1000 + r.nextInt(50000), Arrays.asList(true, false, true));

        int counter = 0;
        StringBuilder sb = new StringBuilder();

        for (Map.Entry<Integer, List<Boolean>> entry : map.entrySet()) {
            sb.append(entry.getKey() + " = " + entry.getValue() + " \t");
            if (counter == 5) {
                System.out.println(sb.toString());
                counter = 0;
                sb.setLength(0);
            }
            counter++;
        }

我使用的是测试数据集,因为真实的数据集太多,更容易小批量工作,但输出看起来完全一样:

30336 = [true, false, true]     2820 = [true, false, false]     17029 = [false, false, false]   10183 = [true, true, false]     14600 = [true, true, false]     9741 = [true, false, true]  
32717 = [true, true, false]     3664 = [true, true, false]  22610 = [true, false, true]     49618 = [true, true, false]     43476 = [true, false, true]     
4439 = [true, false, true]  1816 = [false, false, false]    57562 = [true, false, true]     23450 = [false, true, true]     21018 = [true, false, false]    
35291 = [false, false, false]   40923 = [true, false, false]    5342 = [true, true, false]  32353 = [false, false, false]   48098 = [true, true, false]     
41442 = [false, false, false]   39269 = [true, true, false]     11623 = [false, true, true]     45031 = [true, false, false]    23720 = [true, false, false]    
23209 = [false, false, false]   37419 = [false, true, true]     26798 = [true, false, true]     1454 = [false, true, true]  2544 = [true, false, false]     
12080 = [true, false, false]    26418 = [false, false, false]   24052 = [true, false, true]     50293 = [false, false, false]   25720 = [true, false, true] 

关键的整数范围很大,这让我觉得它没有正确排列,而且“假”这个词比“真”这个词长,所以如果某些条目的值是假的多于真,那么它也会弄乱衬里。但是有没有一种万无一失或简单的方法来解决这个问题?我试过用sb.append(entry.getKey() + " = " + entry.getValue() + " \t");StringUtils.leftPad/rightPadsb.append(entry.getKey() + " = " + entry.getValue() + " \t");,但结果相似,几乎在一条直线上,但呈锯齿状。

【问题讨论】:

    标签: java java-8 pretty-print


    【解决方案1】:

    试试

    sb.append(entry.getKey() + "\t=\t" + entry.getValue() + " \t"); 
    

    【讨论】:

    • 这实际上工作得很好,但是有没有办法说,让 \t 更小?因为在 = 之前和之后似乎有比我想要的更大的空间
    • @IHaveAQuestion 选项卡的大小取决于您的文本编辑器/终端
    • 有没有办法使用 String.Utils right/leftPad 做到这一点?
    【解决方案2】:

    注意,StringBuilder 与字符串连接之类的混用是不一致的

    sb.append(entry.getKey() + " = " + entry.getValue() + " \t");
    

    相反,StringBuilder 应按预期使用,例如

    sb.append(entry.getKey()).append(" = ").append(entry.getValue()).append(" \t");
    

    从那里,。将调用链分解为单独的语句并根据需要插入填充只是一小步:

    final String columnSeparator = "|";
    final int keyWidth = 6, valueWidth = "false, ".length() * 3;
    
    int counter = 0;
    StringBuilder sb = new StringBuilder();
    
    for (Map.Entry<Integer, List<Boolean>> entry : map.entrySet()) {
        int colStart = sb.length(), colEnd = colStart + keyWidth;
        sb.append((int)entry.getKey());
        while(sb.length() < colEnd) sb.insert(colStart, ' '); // right aligned
        sb.append(" = ");
        colEnd = sb.length() + valueWidth;
        sb.append(entry.getValue());
        while(sb.length() < colEnd) sb.append(' '); // left aligned
        if(counter == 5) {
            sb.append(System.lineSeparator());
            counter = 0;
        }
        else {
            sb.append(columnSeparator);
            counter++;
        }
    }
    if(counter != 0) {
        sb.setLength(sb.length() - columnSeparator.length());
        sb.append(System.lineSeparator());
    }
    System.out.append(sb);
    

    产生类似的输出

     14339 = [false, false, false]|  4869 = [true, false, false] | 17669 = [true, false, false] |  3526 = [false, false, false]|  1864 = [false, true, true]  |  9224 = [false, true, true]  
     30152 = [true, true, false]  | 19273 = [false, false, false]| 49547 = [true, true, false]  |  5391 = [true, false, false] |  4943 = [false, false, false]| 39311 = [true, false, true]  
      7889 = [true, false, true]  | 31893 = [false, false, false]| 49174 = [true, true, false]  |  3094 = [true, false, true]  | 16854 = [true, false, true]  | 27094 = [true, false, false] 
     46234 = [true, true, false]  | 22751 = [true, false, false] | 58339 = [false, false, false]| 49700 = [true, false, true]  | 44901 = [true, false, false] | 49254 = [true, false, true]  
     24230 = [true, true, false]  | 45799 = [true, true, false]  | 39591 = [true, false, true]  | 17450 = [false, false, false]| 30893 = [true, false, true]  | 14189 = [true, false, true]  
     33583 = [false, true, true]  | 29365 = [false, false, false]|  5563 = [false, true, true]  | 17341 = [true, true, false]  |  5245 = [true, true, false]  | 18302 = [true, false, false] 
      4223 = [true, false, false] 
    

    您可以根据需要将columnSeparator 更改为" | " 以获得更多间距,或者更改为" " 之类的内容以仅使用空格或空字符串。您还可以将keyWidthvalueWidth 适应现实生活场景。


    上述解决方案在循环中使用sb.insert,这只有在我们知道要移动的字符数很少(它是一个整数)并且迭代次数会很小(这里,我们有四位或五位数字,因此最多迭代两次)。

    当实际密钥长度之间的差异可能更大时,您可以通过在for循环之前插入String pad = " ".repeat(keyWidth);并替换来消除循环

    while(sb.length() < colEnd) sb.insert(colStart, ' ');
    

    if(sb.length() < colEnd) sb.insert(colStart, pad, 0, colEnd - sb.length());
    

    当您还将键类型更改为可能产生相当大字符串的东西时,您可以通过替换完全避免insert

    sb.append((int)entry.getKey());
    while(sb.length() < colEnd) sb.insert(colStart, ' '); // right aligned
    

    String key = String.valueOf(entry.getKey());
    if(key.length() < keyWidth) sb.append(pad, 0, keyWidth - key.length());
    sb.append(key);
    

    但这仅适用于必须通过其toString() 方法附加的对象,而不适用于通过append(int) 附加的Integer 键而无需复制中间字符串。


    如果你有固定的列宽并且想要一个简单的解决方案,你可以使用

    int counter = 0;
    for(Map.Entry<Integer, List<Boolean>> entry : map.entrySet()) {
        System.out.printf(++counter%5 != 0? "%6d = %-21s|": "%6d = %21s%n",
            entry.getKey(), entry.getValue());
    }
    if(counter % 5 != 0) System.out.println();
    

    【讨论】:

    • 嗯,这很棒。但我也想知道,使用 String.format() 可以解决这个问题,你可以给它一个固定的长度吗?我正在尝试使用 String.format 来解决问题,但我对放入 String.format() 的参数感到困惑
    • 是的,当您有固定长度时,您可以使用String.format,但请注意,这只是java.util.Formatter 的外观。当您填充已经存在的StringBuilder 时,值得直接使用目标缓冲区对其进行实例化。如果您更喜欢简单而不是性能,只需使用System.out.printf(…),它是格式化程序的另一个外观,现在将System.out 作为其直接目标。我在答案的末尾添加了代码。缺点是每次调用都会解析格式
    • 所以我使用StringBuilder的困境是因为我必须使用scenario.write。这样做的目的是使用scenario.write将其显示在黄瓜报告上,而scenario.write仅逐行写入,这就是为什么我想做某种连接,然后使用scenario.write在X之后逐行进行串联数量。但是使用 System.out.printf 可能会解决控制台中的问题,我不确定它将如何解决使用scene.write 的黄瓜报告中的问题。这就是为什么我想将它存储到 StringBuilder 中,然后使用scenario.write 将其打印出来
    • 如你所说,那就直接使用Formatterint counter = 0; StringBuilder sb = new StringBuilder(); Formatter f = new Formatter(sb); for(Map.Entry&lt;Integer, List&lt;Boolean&gt;&gt; entry : map.entrySet()) { f.format(++counter != 5? "%6d = %-21s|": "%6d = %21s", entry.getKey(), entry.getValue()); if(counter == 5) { scenario.write(sb); sb.setLength(0); counter = 0; } } if(sb.length() != 0) scenario.write(sb);
    猜你喜欢
    • 2011-05-05
    • 2012-10-24
    • 1970-01-01
    • 2014-11-09
    • 2011-01-02
    • 2022-01-22
    • 1970-01-01
    相关资源
    最近更新 更多