在漂亮的打印表视图Java中排列值

问题描述

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

我用于测试数据集的代码是:

Item.first.update({:popularity => "1"})

我使用的是测试数据集,因为实际的数据集太多,并且更容易在较小的批次中工作,但是输出看起来像:

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));
        map.put(1000 + r.nextInt(50000),true,true));
        map.put(1000 + r.nextInt(50000),false));
        map.put(10000 + r.nextInt(50000),true));
        map.put(1000 + r.nextInt(5000),false));
        map.put(1000 + r.nextInt(5000),true));
        map.put(100 + r.nextInt(50000),true));
        map.put(100 + r.nextInt(5000),true));
        map.put(10000 + r.nextInt(50000),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++;
        }

关键的Integer范围很大,这使我想到它的排列不正确,并且单词“ false”比单词“ true”长,因此,如果某些条目的值是false而不是true ,那么它也会弄乱衬里。但是,有没有万无一失或简单的方法解决此问题?我尝试将30336 = [true,true] 2820 = [true,false] 17029 = [false,false] 10183 = [true,false] 14600 = [true,false] 9741 = [true,true] 32717 = [true,false] 3664 = [true,false] 22610 = [true,true] 49618 = [true,false] 43476 = [true,true] 4439 = [true,true] 1816 = [false,false] 57562 = [true,true] 23450 = [false,true] 21018 = [true,false] 35291 = [false,false] 40923 = [true,false] 5342 = [true,false] 32353 = [false,false] 48098 = [true,false] 41442 = [false,false] 39269 = [true,false] 11623 = [false,true] 45031 = [true,false] 23720 = [true,false] 23209 = [false,false] 37419 = [false,true] 26798 = [true,true] 1454 = [false,true] 2544 = [true,false] 12080 = [true,false] 26418 = [false,false] 24052 = [true,true] 50293 = [false,false] 25720 = [true,true] sb.append(entry.getKey() + " = " + 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] | 17669 = [true,false] |  3526 = [false,false]|  1864 = [false,true,true]  |  9224 = [false,true]  
 30152 = [true,false]  | 19273 = [false,false]| 49547 = [true,false]  |  5391 = [true,false] |  4943 = [false,false]| 39311 = [true,true]  
  7889 = [true,true]  | 31893 = [false,false]| 49174 = [true,false]  |  3094 = [true,true]  | 16854 = [true,true]  | 27094 = [true,false] 
 46234 = [true,false]  | 22751 = [true,false] | 58339 = [false,false]| 49700 = [true,true]  | 44901 = [true,false] | 49254 = [true,true]  
 24230 = [true,false]  | 45799 = [true,false]  | 39591 = [true,true]  | 17450 = [false,false]| 30893 = [true,true]  | 14189 = [true,true]  
 33583 = [false,true]  | 29365 = [false,false]|  5563 = [false,true]  | 17341 = [true,false]  |  5245 = [true,false]  | 18302 = [true,false] 
  4223 = [true,false] 

您可以根据需要将columnSeparator更改为" | "以增加间距,或更改为" "之类以仅使用空格或更改为空字符串。您还可以使keyWidthvalueWidth适应现实情况。


以上解决方案在循环中使用sb.insert,这仅在以下情况下可行:我们知道必须移位的字符数很小(这是整数),并且迭代次数也很小(这里是,我们有四位或五位数字,因此最多重复两次。

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

来消除循环
while(sb.length() < colEnd) sb.insert(colStart,' ');

使用

if(sb.length() < colEnd) sb.insert(colStart,pad,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,keyWidth - key.length());
sb.append(key);

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


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

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

尝试

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