在漂亮的打印中排列值 table 视图 java

Lining up values in pretty print table view java

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

我的测试数据集代码是:

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] 

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

尝试

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

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