StringBuilder 看起来不错 table

Nice looking table with StringBuilder

我知道 "printf" 方法可以使用字符串格式。

我的问题是: 有没有办法用 StringBuilder class 创建一个漂亮的 table?

例如:

|Id|Category|Level|Space|Type|Adress|Dimension|Limits|

在该行下,我必须添加每列的值!

做类似的事情:example 但使用 StringBuilder


所以社区希望看到我的答案(我不明白为什么......但无论如何我都会说!)

public String toString(){
    StringBuilder s = new StringBuilder();

    s.append("Category: "+this.category+"\t");
    s.append("Level: "+this.level.toString()+"\t");

    return s.toString();
}

现在解释一下,为什么看到我的回答会对我有帮助?我真的很想看到你的回答!

当然,一种简单的方法是创建硬连接的 printf 语句。但是,这不是很灵活,因为您总是必须固定列宽,并且函数总是特定于一个 class 及其字段。

所以我想推荐一个助手class,主要做两件事:

  • 封装 table 单元格条目的创建(通过 Java 8 Function
  • 计算给定元素集的每列的最大宽度。

假设有一个给定的模型class,比如Person,例如:

class Person
{
    int getId() { ... }
    String getFirstName() { ... }
    String getLastName() { ... }
    float getHeight()  { ... }
}

然后,我想创建一个 "nice" table 如下:

TableStringBuilder<Person> t = new TableStringBuilder<Person>();
t.addColumn("id", Person::getId);
t.addColumn("first name", Person::getFirstName);
t.addColumn("last name", Person::getLastName);
t.addColumn("height", Person::getHeight);
String s = t.createString(persons);

而且我希望此字符串的内容格式正确 table:

   id|   first name|    last name|height
-----+-------------+-------------+------
41360|Xvnjhpdqdxvcr|    Stvybcwvm|   1.7
 3503|      Krxvzxk|      Xtspsjd|   1.6
41198|       Uegqfl|  Qlocfljbepo|  1.58
26517|       Somyar|       Aopufo|  1.77
13773| Dxehxjbhwgsm|     Jgnlonjv|  1.77
13067|       Zozitk|       Jbozwd|  1.81
46534|        Bosyq|      Kcprrdc|  1.55
93862|    Rlfxblgqp|   Pgrntaqoos|  1.85
12155|   Kjpjlavsqc|Rxfrrollhwhoh|  1.79
75712|        Fwpnd|     Mwcsshwx|  1.78

a MVCE 显示了这样一个 TableStringBuilder 及其应用:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Function;

public class TableStringTest
{
    public static void main(String[] args)
    {
        List<Person> persons = new ArrayList<Person>();
        for (int i=0; i<10; i++)
        {
            persons.add(new Person());
        }

        TableStringBuilder<Person> t = new TableStringBuilder<Person>();
        t.addColumn("id", Person::getId);
        t.addColumn("first name", Person::getFirstName);
        t.addColumn("last name", Person::getLastName);
        t.addColumn("height", Person::getHeight);

        String s = t.createString(persons);
        System.out.println(s);
    }
}


class TableStringBuilder<T>
{
    private final List<String> columnNames;
    private final List<Function<? super T, String>> stringFunctions;

    TableStringBuilder()
    {
        columnNames = new ArrayList<String>();
        stringFunctions = new ArrayList<Function<? super T, String>>();
    }

    void addColumn(String columnName, Function<? super T, ?> fieldFunction)
    {
        columnNames.add(columnName);
        stringFunctions.add((p) -> (String.valueOf(fieldFunction.apply(p))));
    }

    private int computeMaxWidth(int column, Iterable<? extends T> elements)
    {
        int n = columnNames.get(column).length();
        Function<? super T, String> f = stringFunctions.get(column);
        for (T element : elements)
        {
            String s = f.apply(element);
            n = Math.max(n, s.length());
        }
        return n;
    }

    private static String padLeft(String s, char c, int length)
    {
        while (s.length() < length)
        {
            s = c + s;
        }
        return s;
    }

    private List<Integer> computeColumnWidths(Iterable<? extends T> elements)
    {
        List<Integer> columnWidths = new ArrayList<Integer>();
        for (int c=0; c<columnNames.size(); c++)
        {
            int maxWidth = computeMaxWidth(c, elements);
            columnWidths.add(maxWidth);
        }
        return columnWidths;
    }

    public String createString(Iterable<? extends T> elements)
    {
        List<Integer> columnWidths = computeColumnWidths(elements);

        StringBuilder sb = new StringBuilder();
        for (int c=0; c<columnNames.size(); c++)
        {
            if (c > 0)
            {
                sb.append("|");
            }
            String format = "%"+columnWidths.get(c)+"s";
            sb.append(String.format(format, columnNames.get(c)));
        }
        sb.append("\n");
        for (int c=0; c<columnNames.size(); c++)
        {
            if (c > 0)
            {
                sb.append("+");
            }
            sb.append(padLeft("", '-', columnWidths.get(c)));
        }
        sb.append("\n");

        for (T element : elements)
        {
            for (int c=0; c<columnNames.size(); c++)
            {
                if (c > 0)
                {
                    sb.append("|");
                }
                String format = "%"+columnWidths.get(c)+"s";
                Function<? super T, String> f = stringFunctions.get(c);
                String s = f.apply(element);
                sb.append(String.format(format, s));
            }
            sb.append("\n");
        }
        return sb.toString();
    }
}


//Dummy Person Class
class Person
{
    private int id;
    private String firstName;
    private String lastName;
    private float height;

    private static Random random = new Random(0);

    Person()
    {
        id = random.nextInt(100000);
        firstName = createRandomString();
        lastName = createRandomString();
        height = (150 + random.nextInt(40)) / 100.0f;
    }

    private static String createRandomString()
    {
        int length = random.nextInt(10) + 5;
        StringBuilder sb = new StringBuilder();
        char offset = 'A';
        for (int i=0; i<length; i++)
        {
            char c = (char)(random.nextInt(26) + offset);
            sb.append(c);
            offset = 'a';
        }
        return sb.toString();
    }

    int getId()
    {
        return id;
    }

    String getFirstName()
    {
        return firstName;
    }

    String getLastName()
    {
        return lastName;
    }

    float getHeight()
    {
        return height;
    }
}

好的,谢谢大家对我很好的回答。

我发现我们可以那样做:

public String toString(){
    StringBuilder s = new StringBuilder();

    s.append(String.format("%-20s%-20s%-20s%-20s%-20s%-20s%-20s\n","Identifier","Category","Level","Space","Type","Dimension","Limits"));
    s.append(String.format("=============================================================================================================================================\n"));
    for(String id : this.idTable.keySet()) {
        s.append(String.format("%-20s",id));
        s.append(this.idTable.get(id).toString());
        //s.append("Identifier: " +id+" "+this.idTable.get(id).toString()+"\n");
    }
    return s.toString();
}

注意,String.format 完成了我之前不知道的所有工作!

问候你@Marco13,你做得很好 !!!! 谢谢大家!

P.S.: 我会同意@Marco13 的消息,因为他的实现也非常好!