Flip Tables: box drawing tables for Java, and its successor
Because pretty-printing text tables in Java should be easy.
At a glance
- What is it?
- Flip Tables turns arrays of strings into Unicode box drawing tables in one call, with converters for lists, result sets and arbitrary objects, and it handles newlines so tables can nest inside each other. Its author has stopped development and points at picnic instead, so this is now a dependency to read rather than one to build on.
- Who is it for?
- Adopt Flip Tables if you are on Java, you need readable tabular output in a log or a console, and you would rather not write a column width calculation yourself. Do not adopt it for anything new, because the author states that development on this project has basically stopped and points to a successor library, so you would be starting on a project that is not being extended.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One call turns arrays into a box drawn table
Flip Tables is described as existing because pretty-printing text tables in Java should be easy, and the core of the library is a single static factory taking headers and data as strings.
String[] headers = { "Test", "Header" };
String[][] data = {
{ "Foo", "Bar" },
{ "Kit", "Kat" },
};
System.out.println(FlipTable.of(headers, data));The output is a Unicode box drawing table:
โโโโโโโโคโโโโโโโโโ
โ Test โ Header โ
โ โโโโโโโชโโโโโโโโโฃ
โ Foo โ Bar โ
โโโโโโโโผโโโโโโโโโข
โ Kit โ Kat โ
โโโโโโโโงโโโโโโโโโThe interesting detail in that output is the row separator. A single row of data is drawn with the heavy header separator, but the boundary between two data rows is drawn with the lighter junction characters, the โโโโโโโโผโโโโโโโโโข form. That is the convention you see in table output elsewhere and it makes a long table readable, because a reader can tell the header from the body without relying on colour.
The whole point is that the column widths are computed for you. The width of a column is the width of its widest cell, the header included, and the padding is applied to every cell so the box lines up. There is no width argument and no configuration, which is the reason the library is small.
The data is passed as a String[][] rather than as a list of objects, which is a deliberate boundary. The library does the layout, and it does not know what your objects mean.
Empty data and newlines are the two cases that decide the design
Two edge cases shape the whole implementation, and the readme shows both.
The first is empty data. The headers are still required, and the table still gets a body area, but the body holds a single row reading (empty):
โโโโโโโโคโโโโโโโโโ
โ Test โ Header โ
โ โโโโโโโงโโโโโโโโโฃ
โ (empty) โ
โโโโโโโโโโโโโโโโโNote the shape of that frame. The top of the body uses the heavy junction across the whole width, and the bottom is a solid line, because there is no content to divide. The (empty) marker is centred across the body rather than sitting in the first column, and the whole bottom edge is a single run of the fill character with no column divisions, since there is only one logical row. Getting that right is the kind of detail that is invisible when it works and glaring when it does not.
The second case is newlines, and it is the more consequential one. Cell content can contain line breaks, and the table grows taller to fit:
String[] headers = { "One Two\nThree", "Four" };
String[][] data = { { "Five", "Six\nSeven Eight" } };
System.out.println(FlipTable.of(headers, data));The result:
โโโโโโโโโโโคโโโโโโโโโโโโโโ
โ One Two โ Four โ
โ Three โ โ
โ โโโโโโโโโโชโโโโโโโโโโโโโโฃ
โ Five โ Six โ
โ โ Seven Eight โ
โโโโโโโโโโโงโโโโโโโโโโโโโโEach column is padded so its widest line determines the column width, and the header cell's own second line has to fit inside the same width, which is why the header column ends up wider than the word Three needs. The empty continuation cell in the second column on the Three row is padded with spaces to the full width, which is what keeps the vertical rules straight.
Handling embedded newlines is what makes nested tables possible at all, and that is the third feature worth calling out.
Nesting works because a table is just a string
A rendered table is a String, and the library does not stop you putting one inside a cell. The readme shows a table of tables:
String[] innerHeaders = { "One", "Two" };
String[][] innerData = { { "1", "2" } };
String inner = FlipTable.of(innerHeaders, innerData);
String[] headers = { "Left", "Right" };
String[][] data = { { inner, inner } };
System.out.println(FlipTable.of(headers, data));And the output:
โโโโโโโโโโโโโโโโโคโโโโโโโโโโโโโโโโ
โ Left โ Right โ
โ โโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโฃ
โ โโโโโโโคโโโโโโ โ โโโโโโโคโโโโโโ โ
โ โ One โ Two โ โ โ One โ Two โ โ
โ โ โโโโโโชโโโโโโฃ โ โ โโโโโโชโโโโโโฃ โ
โ โ 1 โ 2 โ โ โ 1 โ 2 โ โ
โ โโโโโโโงโโโโโโ โ โโโโโโโงโโโโโโ โ
โโโโโโโโโโโโโโโโโงโโโโโโโโโโโโโโโโThis is the payoff of the newline handling. Because the inner table is a multi-line string, the outer table's cell measurement logic treats it as a cell with several lines, and the outer box is drawn around the block. The vertical rules of the outer table continue past the nested table, and the nested table's own rules are inset within the cell.
The alignment inside the nested table follows from the same rule that produced the multi-line header: each of the inner table's lines is padded to the width of the widest inner line, and the outer table then pads each of those lines to the width of the cell. The result is that the inner box does not quite touch the outer rules, and there is a single space of padding on each side. That is a consequence of the implementation rather than a design decision someone made, and it is the sort of detail worth knowing before you embed one table inside another in a log message.
The practical use is a report or a debug dump where a row carries a structured payload, and a nested table reads better than a flattened one.
Converters for lists, result sets and plain objects
The core factory takes string arrays, so the library ships a separate set of converters for the shapes you actually have.
For a list of objects with bean style getters, FlipTableConverters.fromIterable takes the list and the class:
List<Person> people = Arrays.asList(new Person("Foo", "Bar"), new Person("Kit", "Kat"));
System.out.println(FlipTableConverters.fromIterable(people, Person.class));The headers are derived from the property names, capitalised, and the output shows FirstName and LastName:
โโโโโโโโโโโโโคโโโโโโโโโโโ
โ FirstName โ LastName โ
โ โโโโโโโโโโโโชโโโโโโโโโโโฃ
โ Foo โ Bar โ
โโโโโโโโโโโโโผโโโโโโโโโโโข
โ Kit โ Kat โ
โโโโโโโโโโโโโงโโโโโโโโโโโFor a JDBC result set, the converter reads the column metadata:
ResultSet resultSet = statement.executeQuery("SELECT first_name, last_name FROM users");
System.out.println(FlipTableConverters.fromResultSet(resultSet));and the headers are the column names as the database has them, so first_name and last_name, with the values in the rows:
โโโโโโโโโโโโโโคโโโโโโโโโโโโ
โ first_name โ last_name โ
โ โโโโโโโโโโโโโชโโโโโโโโโโโโฃ
โ Jake โ Wharton โ
โโโโโโโโโโโโโโผโโโโโโโโโโโโข
โ Edward โ Snowden โ
โโโโโโโโโโโโโโงโโโโโโโโโโโโFor arbitrary objects where you want to choose the columns yourself, there is a third converter that takes explicit headers and an object array:
String[] headers = { "First Name", "Last Name", "Age", "Type" };
Object[][] data = {
{ "Big", "Bird", 16, PersonType.COSTUME },
{ "Joe", "Smith", 42, PersonType.HUMAN },
{ "Oscar", "Grouchant", 8, PersonType.PUPPET }
};
System.out.println(FlipTableConverters.fromObjects(headers, data));The output puts the enum's name in the Type column and right-aligns the numeric Age column, which is a nice detail that the string-based factory cannot do because it never sees a number:
โโโโโโโโโโโโโโคโโโโโโโโโโโโคโโโโโโคโโโโโโโโโโ
โ First Name โ Last Name โ Age โ Type โ
โ โโโโโโโโโโโโโชโโโโโโโโโโโโชโโโโโโชโโโโโโโโโโฃ
โ Big โ Bird โ 16 โ COSTUME โ
โโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโผโโโโโโโโโโข
โ Joe โ Smith โ 42 โ HUMAN โ
โโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโผโโโโโโโโโโข
โ Oscar โ Grouchant โ 8 โ PUPPET โ
โโโโโโโโโโโโโโงโโโโโโโโโโโโงโโโโโโงโโโโโโโโโโSo the three converters split cleanly by how much you want to say about your data: the result set converter takes the schema from the database, the iterable converter takes the schema from the class, and the objects converter takes the schema from you.
The author recommends a different library now
The most useful line in the readme is the second one, and it changes how you should read everything else.
The readme says that development on this project has basically stopped, and points at a more comprehensive text table library at a different repository on the same GitHub account, picnic. The advice is to find that library instead.
This is worth taking at face value rather than treating as a formality. A library whose author has publicly said development has stopped is a library where you will not get a fix for a bug you find, and where an API change would only ever be made to solve a problem in a project you are not using. The readme also notes that snapshots of the development version are available in a repository, which is a small detail that quietly confirms there is a development version but that it is not the thing to build on.
So the sensible reading is that Flip Tables is a completed, working, small library that is now in maintenance mode, and picnic is where new work goes. Both are by the same author and cover the same problem area, so the comparison you would make is not which one renders a table better but which one fits the job, and for that the successor is the one to read.
The distribution facts are simple. The library is on Maven Central as com.jakewharton.fliptables:fliptables:1.1.1, with the jar downloadable from a search.maven.org link and the development snapshots in the Central Portal snapshots repository. The only tagged release is 1.1.1 from 2024-01-24, and the last push to the repository was on 2026-10-14.
The licence is Apache 2.0, the copyright line reads Copyright 2014 Jake Wharton, and the project is a Gradle build with a settings.gradle, a build.gradle, a gradle.properties and a wrapper, plus a src directory, a CHANGELOG and a RELEASING.md describing how releases are cut.
Editorial conclusion
Adopt Flip Tables if you are on Java, you need readable tabular output in a log or a console, and you would rather not write a column width calculation yourself. Do not adopt it for anything new, because the author states that development on this project has basically stopped and points to a successor library, so you would be starting on a project that is not being extended. Before you pick it up, look at the successor project the readme names, and if you do use Flip Tables, check whether the newline handling and the empty data case behave the way your log consumers expect.
Frequently asked questions
What does Flip Tables do?
It pretty prints text tables in Java. A FlipTable requires headers and data in string form, and the library computes the column widths and draws the table with Unicode box drawing characters. Newlines inside cell values are supported, which is what allows tables to be nested inside each other.
How do I print a table from a JDBC result set with Flip Tables?
Use FlipTableConverters.fromResultSet with the result set. The headers are taken from the database column names, so a query selecting first_name and last_name produces those two column headings.
Is Flip Tables still being developed?
No. The readme states that development on the project has basically stopped and directs readers to a more comprehensive text table library by the same author, hosted at github.com/JakeWharton/picnic. The only tagged release is 1.1.1 from 2024-01-24.
How do I add Flip Tables to a Java project?
Reference it from Maven Central as com.jakewharton.fliptables:fliptables:1.1.1, or download the latest jar directly. Snapshots of the development version are published in the Central Portal snapshots repository.
What happens when I print a Flip Table with no data rows?
The headers are still drawn, and the body holds a single row reading (empty), with the body top edge drawn as one heavy junction and the bottom as a solid line with no column divisions, since there is only one logical row.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jakewharton-flip-tables)Was this useful?