title: Swing - sort a JTable's rows but keep them grouped by one column and only show the value once on top of each group
body: <p>I want to sort a <code>JTable</code> like this: </p>
  
  <p><a href="https://i.stack.imgur.com/qqFH2.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/qqFH2.png" alt="enter image description here"></a></p>
  
  <p>That is: (<strong>EDIT</strong>)</p>
  
  <ol>
  <li><p>If I click the "Name" column to sort, "Tom", "Polazzo" and "Anna" must be sorted alphabetically, and rows with the same names must stay together("grouped" by the name), and each name should be shown only once, the rest cells must be blank.</p></li>
  <li><p>If I click the "Duration" or "Book #" column, I want all rows sorted ascending/descending by values of duration/book number, but same as in point 1), rows with the same "Name" must stay together, that is, stay grouped, and only the first row in every group is shown, and the rest "Name" stay blank.</p></li>
  </ol>
  
  <p>The data in the table model's vector are collected from parsing a XML file. The rows with same "Name" are under the same node in the hierarchy tree.</p>
  
  <p>I think there're two ways to do this:</p>
  
  <p>a) When collecting the data and construct the rows, under the same "Name" node, give the cell at column 0 the "Name" value, and leave the rest of rows "" in the same column. But, I don't know how to construct the comparator of column "Name", to ensure the first row always being the top in sorting. (It cannot be the biggest and the smallest when we override <code>compare()</code> method, can it?)</p>
  
  <p>b) Every time we click the table header to sort, make the renderer repaint the table the way we want: comparing the value in the first line of each group, and if it's the same as the last line's column 0's value, don't paint this cell, until we reach another different value. In that way, we don't mess with comparators/sorters, and it turns into a renderer problem. That's what I kind of achieved in the SSCCE below, but I am half way there and I need some tips.</p>
  
  <pre><code>import java.awt.BorderLayout;
  import java.awt.Component;
  import java.awt.List;
  import java.awt.event.MouseEvent;
  import java.awt.event.MouseListener;
  import java.util.ArrayList;
  import java.util.Comparator;
  
  import javax.swing.JFrame;
  import javax.swing.JLabel;
  import javax.swing.JScrollPane;
  import javax.swing.JTable;
  import javax.swing.RowSorter;
  import javax.swing.RowSorter.SortKey;
  import javax.swing.event.RowSorterEvent;
  import javax.swing.event.RowSorterListener;
  import javax.swing.SortOrder;
  import javax.swing.SwingUtilities;
  import javax.swing.table.DefaultTableModel;
  import javax.swing.table.JTableHeader;
  import javax.swing.table.TableCellRenderer;
  import javax.swing.table.TableModel;
  import javax.swing.table.TableRowSorter;
  
  import com.WindThunderStudio.JHeaderToolTip.JHeaderToolTip;
  
  import net.miginfocom.swing.MigLayout;
  
  public class RowGroupInTable extends JFrame {
      public RowGroupInTable() {
          begin();
      }
  
      private void begin() {
          setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  
          setLayout(new BorderLayout());
  
          DefaultTableModel model = new DefaultTableModel();
          Object[][] data = new Object[][] {{"Tom", "17", "Book1"},
              {"Tom", 23, "Book2"}, 
              {"Tom", 25, "Book3"},
              {"Polazzo", 41, "Book1"}, 
              {"Polazzo", 45, "Book2"},
              {"Polazzo", 12, "Book3"},
              {"Anna", 1, "Book3"}, 
              {"Anna", 33, "Book5"}};
  
          String[] titles = new String[] {"Name", "Last job duration", "Book #"};
          JTable table = new JTable(data, titles);
          table.setFillsViewportHeight(true);
  
          table.setAutoCreateRowSorter(false);
  
          TableRowSorter&lt;TableModel&gt; sorter = new TableRowSorter&lt;TableModel&gt;(table.getModel());
          ArrayList&lt;SortKey&gt; sortKeys = new ArrayList&lt;RowSorter.SortKey&gt;();
          sortKeys.add(new SortKey(2, SortOrder.ASCENDING));
          sortKeys.add(new SortKey(1, SortOrder.ASCENDING));
  
  //        sorter.setSortKeys(sortKeys);
          sorter.setSortable(0, true);
          sorter.setSortable(1, false);
          sorter.setSortable(2, true);
  
          table.setRowSorter(sorter);
  
          table.setDefaultRenderer(Object.class, new MyRenderer(table.getDefaultRenderer(Object.class)));
          JTableHeader header = table.getTableHeader();
          header.addMouseListener(new MouseListener() {
  
              @Override
              public void mouseReleased(MouseEvent e) {
                  // TODO Auto-generated method stub
  
              }
  
              @Override
              public void mousePressed(MouseEvent e) {
                  // TODO Auto-generated method stub
  
              }
  
              @Override
              public void mouseExited(MouseEvent e) {
                  // TODO Auto-generated method stub
  
              }
  
              @Override
              public void mouseEntered(MouseEvent e) {
                  // TODO Auto-generated method stub
  
              }
  
              @Override
              public void mouseClicked(MouseEvent e) {
                  int col = ((JTableHeader)(e.getComponent())).getColumnModel().getColumnIndexAtX(e.getX());
  
              }
          });
  
          JScrollPane sp = new JScrollPane(table);
          sp.setBounds(0, 0, 200, 200);
  
          add(sp, BorderLayout.CENTER);
  
          pack();
          setLocationRelativeTo(null);
          setVisible(true);
      }
  
      public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
  
              @Override
              public void run() {
                  RowGroupInTable frame = new RowGroupInTable();
  
              }
  
          });
      }
  
      private class MyRenderer implements TableCellRenderer {
  
          TableCellRenderer def;
          public MyRenderer() {
              // TODO Auto-generated constructor stub
          }
  
          public MyRenderer(TableCellRenderer defaultRend) {
              this();
              this.def = defaultRend;
          }
          @Override
          public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
                  int row, int column) {
              int rowCount = table.getModel().getRowCount();
  
              Component orig = (def).getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
              if (column == 0) {
                  if (row == 0) {
                      return orig;
                  } else if (row &gt; 0 &amp;&amp; row &lt; rowCount) {
                      if (table.getModel().getValueAt(row-1, column).equals(value)) {
                          return new JLabel("");
                      } else {
                          return orig;
                      }
                  }
              }
  
              return orig;
          }
  
      }
  }
  </code></pre>