好的,这就去。这是使用番石榴的解决方案
首先这是一个辅助类Table
(不要与 Guava 的Table
接口混淆):
public class Table {
private static final MapSplitter MAP_SPLITTER =
Splitter.on(',').trimResults().withKeyValueSeparator(Splitter.on('='));
private final Multimap<String, String> values =
Multimaps.newListMultimap(
// keys are sorted, values maintain insertion order
Maps.<String, Collection<String>>newTreeMap(),
new Supplier<List<String>>() {
@Override public List<String> get() {
return Lists.newArrayList();
}
});
// keys are sorted, to correspond with the order of the other map
private final Map<String, Integer> lengths = Maps.newTreeMap();
private int modCount = 0;
public void addRow(final String row) {
modCount++;
final Map<String, String> lineData = MAP_SPLITTER.split(row);
final boolean empty = values.isEmpty();
if (!empty && values.keySet().size() != lineData.size()) {
throw new IllegalArgumentException("Bad row: " + row);
}
for (final Entry<String, String> entry : lineData.entrySet()) {
final String key = entry.getKey();
final String value = entry.getValue();
if (!empty && !values.containsKey(key)) {
throw new IllegalArgumentException(
"Bad column: " + key + " in row " + row);
}
final Integer tempLength;
if (empty) {
tempLength = key.length();
} else {
tempLength = lengths.get(key);
}
values.put(key, value);
lengths.put(key, Math.max(value.length(), tempLength));
}
}
public Iterable<String> getHeaders() {
return Collections.unmodifiableSet(values.asMap().keySet());
}
public Iterable<Integer> getColumnSizes() {
return Collections.unmodifiableCollection(lengths.values());
}
public Iterable<Iterable<String>> getData() {
return new Iterable<Iterable<String>>() {
@Override
public Iterator<Iterable<String>> iterator() {
return new RowIterator();
}
};
}
private class RowIterator extends AbstractIterator<Iterable<String>> {
private int rowIndex = -1;
private final int modCount = Table.this.modCount;
private final int maxRow =
values.asMap().values().iterator().next().size() - 1;
@Override
protected Iterable<String> computeNext() {
if (Table.this.modCount != modCount) {
throw new ConcurrentModificationException();
}
final Map<String, Collection<String>> map = values.asMap();
if (rowIndex++ == maxRow) {
return endOfData();
}
final List<String> data =
Lists.newArrayListWithCapacity(map.size());
for (final Collection<String> column : map.values()) {
data.add(((List<String>) column).get(rowIndex));
}
return Collections.unmodifiableCollection(data);
}
}
}
现在我们可以使用这个表类来格式化你的数据:
final String input = "columns=20xs, viewport_supported=true, wta=false, mmf=false\n"
+ "columns=11xs, viewport_supported=false, wta=false, mmf=true \n"
+ "columns=15xs, viewport_supported=true, wta=false, mmf=false";
final Table table = new Table();
final Iterable<String> lines = Splitter.on('\n').trimResults().split(input);
for (final String line : lines) {
// add one row of data
table.addRow(line);
}
// Using Appendable so you can easily switch to some other implementation,
// e.g. System.out
final Appendable appendable = new StringBuilder();
final Iterable<Integer> columnSizes = table.getColumnSizes();
final Iterable<String> headers = table.getHeaders();
final Iterator<String> headerIterator = headers.iterator();
final Iterable<Iterable<String>> data = table.getData();
{
// write headers
boolean first = true;
for (final Integer size : columnSizes) {
if (first) {
first = false;
} else {
appendable.append(" | ");
}
appendable.append(Strings.padEnd(headerIterator.next(), size, ' '));
}
appendable.append('\n');
}
{
// underline headers
boolean first = true;
for (final Integer size : columnSizes) {
if (first) {
first = false;
} else {
appendable.append("-+-");
}
appendable.append(Strings.repeat("-", size));
}
appendable.append('\n');
}
// write data
for (final Iterable<String> row : data) {
boolean first = true;
final Iterator<String> rowIterator = row.iterator();
for (final Integer size : columnSizes) {
if (first) {
first = false;
} else {
appendable.append(" | ");
}
appendable.append(Strings.padEnd(rowIterator.next(), size, ' '));
}
appendable.append('\n');
}
System.out.println(appendable);
这是输出:
columns | mmf | viewport_supported | wta
--------+-------+--------------------+------
20xs | false | true | false
11xs | true | false | false
15xs | false | true | false
如果您的行具有可变内容(并非所有行都包含相同的列),这将更加困难,这就是我强制所有行必须包含相同列的原因。随意改变它(或其他任何东西)。