Implementing a tabular pretty-print using Java 8 Stream API
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
3
down vote
favorite
About six years ago I implemented a simple tabular pretty-print Java class that mostly simulated MySQL CLI query result tables.
I don't really like it because it was implemented in a, I believe, pretty dirty manner being not extensible, and I would assume that the same thing might be implemented using Java 8 Stream API that might be a bit more generic.
The following implementation is just a matter of reimplementation interest and I'm trying to learn writing some Spliterator
s.
The source code:
public final class Table
private Table()
public static Collector<String, ?, Stream<String>> toTable()
return Collector.of(
State::new,
State::addRow,
State::mergeState,
State::stream
);
public static Collector<String, ?, Stream<String>> toTable(final String... columnHeaders)
return Collector.of(
() ->
final State state = new State();
state.addRow(columnHeaders);
return state;
,
State::addRow,
State::mergeState,
State::stream
);
private static final class State
private static final int emptyIntArray = ;
private final Collection<String> rows = new ArrayList<>();
private int maxColumnWidths = emptyIntArray;
private State()
private void addRow(final String... values)
ensureColumnMaxLengths(values);
recalculateWidths(values);
rows.add(values);
private State mergeState(final State stateR)
for ( final String row : stateR.rows )
addRow(row);
return this;
private void ensureColumnMaxLengths(final String... values)
if ( maxColumnWidths.length < values.length )
final int newWidths = new int[values.length];
System.arraycopy(maxColumnWidths, 0, newWidths, 0, maxColumnWidths.length);
maxColumnWidths = newWidths;
private void recalculateWidths(final String... values)
final int count = values.length;
for ( int i = 0; i < count; i++ )
final String value = values[i];
final int valueLength = value.length();
if ( valueLength > maxColumnWidths[i] )
maxColumnWidths[i] = valueLength;
private Stream<String> stream()
return StreamSupport.stream(FormattedRowSpliterator.get(rows, maxColumnWidths), false);
private static final class FormattedRowSpliterator
implements Spliterator<String>
private static final char KNOT = '+';
private static final char VERTICAL = '
Example of use:
public static void main(final String... args)
Stream.generate(() -> new int random(), random(), random() )
.limit(5)
.map(row -> new String Integer.toString(row[0]), Integer.toString(row[1]), Integer.toString(row[2]) )
.collect(Table.toTable("value 1"))
.forEach(System.out::println);
private static int random()
final int digit = (int) (Math.random() * 10);
return (int) Math.pow(10, digit - 1) * digit;
Example result:
+-------+---------+--------+
|value 1|value 2 |value 3 |
+-------+---------+--------+
|300 |900000000|50000 |
+-------+---------+--------+
|600000 |900000000|80000000|
+-------+---------+--------+
|20 |900000000|80000000|
+-------+---------+--------+
|4000 |7000000 |4000 |
+-------+---------+--------+
|1 |20 |80000000|
+-------+---------+--------+
Known limitations:
- The implementation does not deal with
null
s by design (and I do believe this is fine, however the code above does not check for inputnull
s and may throw "obscure" exceptions). - This implementation is not designed to be extensible (for example, its design is not really suitable for CSV streams since the current tabular representation must scan the whole table in advance whereas CSV does not require it and may transform/render infinite data streams).
My concerns at least are:
- This implementation renders the formatted strings and table borders as whole strings. Probably rendering the each cell separately might be a better or more optimal idea, but I don't know how it would affect the
n
character then. - I don't know how fine is the implemented spliterator, especially its
characteristics()
method. - I'm not 100% sure if dynamic expanding the
maxColumnWidths
array is a good idea. For example, I would assume that the collector must only expect well-formed data, and detecting jagged rows is basically out of the scope of the collector (simply speaking, it probably should always expect for n x m matrices). - Well, a collector that produces a stream.
Any corrections and suggestions are welcome. Thank you.
java iterator
add a comment |Â
up vote
3
down vote
favorite
About six years ago I implemented a simple tabular pretty-print Java class that mostly simulated MySQL CLI query result tables.
I don't really like it because it was implemented in a, I believe, pretty dirty manner being not extensible, and I would assume that the same thing might be implemented using Java 8 Stream API that might be a bit more generic.
The following implementation is just a matter of reimplementation interest and I'm trying to learn writing some Spliterator
s.
The source code:
public final class Table
private Table()
public static Collector<String, ?, Stream<String>> toTable()
return Collector.of(
State::new,
State::addRow,
State::mergeState,
State::stream
);
public static Collector<String, ?, Stream<String>> toTable(final String... columnHeaders)
return Collector.of(
() ->
final State state = new State();
state.addRow(columnHeaders);
return state;
,
State::addRow,
State::mergeState,
State::stream
);
private static final class State
private static final int emptyIntArray = ;
private final Collection<String> rows = new ArrayList<>();
private int maxColumnWidths = emptyIntArray;
private State()
private void addRow(final String... values)
ensureColumnMaxLengths(values);
recalculateWidths(values);
rows.add(values);
private State mergeState(final State stateR)
for ( final String row : stateR.rows )
addRow(row);
return this;
private void ensureColumnMaxLengths(final String... values)
if ( maxColumnWidths.length < values.length )
final int newWidths = new int[values.length];
System.arraycopy(maxColumnWidths, 0, newWidths, 0, maxColumnWidths.length);
maxColumnWidths = newWidths;
private void recalculateWidths(final String... values)
final int count = values.length;
for ( int i = 0; i < count; i++ )
final String value = values[i];
final int valueLength = value.length();
if ( valueLength > maxColumnWidths[i] )
maxColumnWidths[i] = valueLength;
private Stream<String> stream()
return StreamSupport.stream(FormattedRowSpliterator.get(rows, maxColumnWidths), false);
private static final class FormattedRowSpliterator
implements Spliterator<String>
private static final char KNOT = '+';
private static final char VERTICAL = '
Example of use:
public static void main(final String... args)
Stream.generate(() -> new int random(), random(), random() )
.limit(5)
.map(row -> new String Integer.toString(row[0]), Integer.toString(row[1]), Integer.toString(row[2]) )
.collect(Table.toTable("value 1"))
.forEach(System.out::println);
private static int random()
final int digit = (int) (Math.random() * 10);
return (int) Math.pow(10, digit - 1) * digit;
Example result:
+-------+---------+--------+
|value 1|value 2 |value 3 |
+-------+---------+--------+
|300 |900000000|50000 |
+-------+---------+--------+
|600000 |900000000|80000000|
+-------+---------+--------+
|20 |900000000|80000000|
+-------+---------+--------+
|4000 |7000000 |4000 |
+-------+---------+--------+
|1 |20 |80000000|
+-------+---------+--------+
Known limitations:
- The implementation does not deal with
null
s by design (and I do believe this is fine, however the code above does not check for inputnull
s and may throw "obscure" exceptions). - This implementation is not designed to be extensible (for example, its design is not really suitable for CSV streams since the current tabular representation must scan the whole table in advance whereas CSV does not require it and may transform/render infinite data streams).
My concerns at least are:
- This implementation renders the formatted strings and table borders as whole strings. Probably rendering the each cell separately might be a better or more optimal idea, but I don't know how it would affect the
n
character then. - I don't know how fine is the implemented spliterator, especially its
characteristics()
method. - I'm not 100% sure if dynamic expanding the
maxColumnWidths
array is a good idea. For example, I would assume that the collector must only expect well-formed data, and detecting jagged rows is basically out of the scope of the collector (simply speaking, it probably should always expect for n x m matrices). - Well, a collector that produces a stream.
Any corrections and suggestions are welcome. Thank you.
java iterator
add a comment |Â
up vote
3
down vote
favorite
up vote
3
down vote
favorite
About six years ago I implemented a simple tabular pretty-print Java class that mostly simulated MySQL CLI query result tables.
I don't really like it because it was implemented in a, I believe, pretty dirty manner being not extensible, and I would assume that the same thing might be implemented using Java 8 Stream API that might be a bit more generic.
The following implementation is just a matter of reimplementation interest and I'm trying to learn writing some Spliterator
s.
The source code:
public final class Table
private Table()
public static Collector<String, ?, Stream<String>> toTable()
return Collector.of(
State::new,
State::addRow,
State::mergeState,
State::stream
);
public static Collector<String, ?, Stream<String>> toTable(final String... columnHeaders)
return Collector.of(
() ->
final State state = new State();
state.addRow(columnHeaders);
return state;
,
State::addRow,
State::mergeState,
State::stream
);
private static final class State
private static final int emptyIntArray = ;
private final Collection<String> rows = new ArrayList<>();
private int maxColumnWidths = emptyIntArray;
private State()
private void addRow(final String... values)
ensureColumnMaxLengths(values);
recalculateWidths(values);
rows.add(values);
private State mergeState(final State stateR)
for ( final String row : stateR.rows )
addRow(row);
return this;
private void ensureColumnMaxLengths(final String... values)
if ( maxColumnWidths.length < values.length )
final int newWidths = new int[values.length];
System.arraycopy(maxColumnWidths, 0, newWidths, 0, maxColumnWidths.length);
maxColumnWidths = newWidths;
private void recalculateWidths(final String... values)
final int count = values.length;
for ( int i = 0; i < count; i++ )
final String value = values[i];
final int valueLength = value.length();
if ( valueLength > maxColumnWidths[i] )
maxColumnWidths[i] = valueLength;
private Stream<String> stream()
return StreamSupport.stream(FormattedRowSpliterator.get(rows, maxColumnWidths), false);
private static final class FormattedRowSpliterator
implements Spliterator<String>
private static final char KNOT = '+';
private static final char VERTICAL = '
Example of use:
public static void main(final String... args)
Stream.generate(() -> new int random(), random(), random() )
.limit(5)
.map(row -> new String Integer.toString(row[0]), Integer.toString(row[1]), Integer.toString(row[2]) )
.collect(Table.toTable("value 1"))
.forEach(System.out::println);
private static int random()
final int digit = (int) (Math.random() * 10);
return (int) Math.pow(10, digit - 1) * digit;
Example result:
+-------+---------+--------+
|value 1|value 2 |value 3 |
+-------+---------+--------+
|300 |900000000|50000 |
+-------+---------+--------+
|600000 |900000000|80000000|
+-------+---------+--------+
|20 |900000000|80000000|
+-------+---------+--------+
|4000 |7000000 |4000 |
+-------+---------+--------+
|1 |20 |80000000|
+-------+---------+--------+
Known limitations:
- The implementation does not deal with
null
s by design (and I do believe this is fine, however the code above does not check for inputnull
s and may throw "obscure" exceptions). - This implementation is not designed to be extensible (for example, its design is not really suitable for CSV streams since the current tabular representation must scan the whole table in advance whereas CSV does not require it and may transform/render infinite data streams).
My concerns at least are:
- This implementation renders the formatted strings and table borders as whole strings. Probably rendering the each cell separately might be a better or more optimal idea, but I don't know how it would affect the
n
character then. - I don't know how fine is the implemented spliterator, especially its
characteristics()
method. - I'm not 100% sure if dynamic expanding the
maxColumnWidths
array is a good idea. For example, I would assume that the collector must only expect well-formed data, and detecting jagged rows is basically out of the scope of the collector (simply speaking, it probably should always expect for n x m matrices). - Well, a collector that produces a stream.
Any corrections and suggestions are welcome. Thank you.
java iterator
About six years ago I implemented a simple tabular pretty-print Java class that mostly simulated MySQL CLI query result tables.
I don't really like it because it was implemented in a, I believe, pretty dirty manner being not extensible, and I would assume that the same thing might be implemented using Java 8 Stream API that might be a bit more generic.
The following implementation is just a matter of reimplementation interest and I'm trying to learn writing some Spliterator
s.
The source code:
public final class Table
private Table()
public static Collector<String, ?, Stream<String>> toTable()
return Collector.of(
State::new,
State::addRow,
State::mergeState,
State::stream
);
public static Collector<String, ?, Stream<String>> toTable(final String... columnHeaders)
return Collector.of(
() ->
final State state = new State();
state.addRow(columnHeaders);
return state;
,
State::addRow,
State::mergeState,
State::stream
);
private static final class State
private static final int emptyIntArray = ;
private final Collection<String> rows = new ArrayList<>();
private int maxColumnWidths = emptyIntArray;
private State()
private void addRow(final String... values)
ensureColumnMaxLengths(values);
recalculateWidths(values);
rows.add(values);
private State mergeState(final State stateR)
for ( final String row : stateR.rows )
addRow(row);
return this;
private void ensureColumnMaxLengths(final String... values)
if ( maxColumnWidths.length < values.length )
final int newWidths = new int[values.length];
System.arraycopy(maxColumnWidths, 0, newWidths, 0, maxColumnWidths.length);
maxColumnWidths = newWidths;
private void recalculateWidths(final String... values)
final int count = values.length;
for ( int i = 0; i < count; i++ )
final String value = values[i];
final int valueLength = value.length();
if ( valueLength > maxColumnWidths[i] )
maxColumnWidths[i] = valueLength;
private Stream<String> stream()
return StreamSupport.stream(FormattedRowSpliterator.get(rows, maxColumnWidths), false);
private static final class FormattedRowSpliterator
implements Spliterator<String>
private static final char KNOT = '+';
private static final char VERTICAL = '
Example of use:
public static void main(final String... args)
Stream.generate(() -> new int random(), random(), random() )
.limit(5)
.map(row -> new String Integer.toString(row[0]), Integer.toString(row[1]), Integer.toString(row[2]) )
.collect(Table.toTable("value 1"))
.forEach(System.out::println);
private static int random()
final int digit = (int) (Math.random() * 10);
return (int) Math.pow(10, digit - 1) * digit;
Example result:
+-------+---------+--------+
|value 1|value 2 |value 3 |
+-------+---------+--------+
|300 |900000000|50000 |
+-------+---------+--------+
|600000 |900000000|80000000|
+-------+---------+--------+
|20 |900000000|80000000|
+-------+---------+--------+
|4000 |7000000 |4000 |
+-------+---------+--------+
|1 |20 |80000000|
+-------+---------+--------+
Known limitations:
- The implementation does not deal with
null
s by design (and I do believe this is fine, however the code above does not check for inputnull
s and may throw "obscure" exceptions). - This implementation is not designed to be extensible (for example, its design is not really suitable for CSV streams since the current tabular representation must scan the whole table in advance whereas CSV does not require it and may transform/render infinite data streams).
My concerns at least are:
- This implementation renders the formatted strings and table borders as whole strings. Probably rendering the each cell separately might be a better or more optimal idea, but I don't know how it would affect the
n
character then. - I don't know how fine is the implemented spliterator, especially its
characteristics()
method. - I'm not 100% sure if dynamic expanding the
maxColumnWidths
array is a good idea. For example, I would assume that the collector must only expect well-formed data, and detecting jagged rows is basically out of the scope of the collector (simply speaking, it probably should always expect for n x m matrices). - Well, a collector that produces a stream.
Any corrections and suggestions are welcome. Thank you.
java iterator
asked Mar 2 at 12:19
Lyubomyr Shaydariv
23117
23117
add a comment |Â
add a comment |Â
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f188673%2fimplementing-a-tabular-pretty-print-using-java-8-stream-api%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password