javaswingjtabletablerowsorter

JTable keeps sorting numbers as Strings


I want to sort the table correctly comparing the correct classes, not strings. I'm using a TableRowSorter:

TableRowSorter sorter = new TableRowSorter<CustomModelTable>(modelData);
tablaEJ.setRowSorter(sorter);

I overrided the method getColumnClass in CustomModelTable so I get the correct class depending on the column's name (this was made because types are defined programatically):

@Override
    public Class getColumnClass(int columnIndex)
    {
        try {
            if (columnIndex < this.getColumnCount()) {
                String name = getColumnName(columnIndex); //This just gets the header of the column
                name = name.toLowerCase();
                switch (name) {
                    case "int":
                    case "int[]":
                    case "núm.":
                    case "int[][]": return Integer.class; //Or Class.forName("java.lang.Integer");
                    case "byte":
                    case "byte[]":
                    case "byte[][]": return Class.forName("java.lang.Byte");
                    case "long":
                    case "long[]":
                    case "long[][]": return Class.forName("java.lang.Long");
                    case "float":
                    case "float[]":
                    case "float[][]": return Class.forName("java.lang.Float");
                    case "double":
                    case "double[]":
                    case "double[][]": return Class.forName("java.lang.Double");
                    case "boolean":
                    case "boolean[]":
                    case "boolean[][]": return Class.forName("java.lang.Boolean");
                    case "char":
                    case "char[]":
                    case "char[][]": return Class.forName("java.lang.Character");
                    case "string":
                    case "string[]":
                    case "string[][]": return Class.forName("java.lang.String");
                    default: return Class.forName("java.lang.String");
                }
            }
        } catch (Exception e){

        }
        return null;
    }

Here's an screenshot: Table wrongly sorted

The most curious thing is that I print the types:

for (int i = 0; i < tablaEJ.getModel().getColumnCount(); i++) {
     System.out.println(tablaEJ.getModel().getColumnClass(i));
}

And it returns the expected output (print of the above table):

class java.lang.Integer
class java.lang.Integer
class java.lang.Integer
class java.lang.Integer

Honestly, I don't know what I'm doing wrong.


Solution

  • The "safe" way is to declare which column indices are numbers:

    public static class CustomTableModel extends DefaultTableModel {
            private final List<Integer> columnIndicesWithNumbers = Arrays.asList(0);
    
            @Override
            public Class<?> getColumnClass(int columnIndex) {
                if (columnIndicesWithNumbers.contains(columnIndex))
                    return Double.class;
                return super.getColumnClass(columnIndex);
            }
        }
    

    Here is a complete example to test yourself:

    public class SorterTest {
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> {
                DefaultTableModel dm = new CustomTableModel();
                dm.addColumn("Integers");
                dm.addColumn("Strings");
                //@formatter:off 
                Object[][] data = { 
                        { 1, "Something" }, { 102, "Something Else" }, 
                        { 55, "Something" }, { 66, "Something" },
                        { 1000, "Something" }, { 1524, "Something" }, 
                        { 5801, "Something" }, { -55, "Something" },};
                //@formatter:on
                for (Object[] row : data) {
                    dm.addRow(row);
                }
                JTable table = new JTable(dm);
                table.setAutoCreateRowSorter(true);
    
                JOptionPane.showMessageDialog(null, new JScrollPane(table));
            });
        }
    
        public static class CustomTableModel extends DefaultTableModel {
            private final List<Integer> columnIndicesWithNumbers = Arrays.asList(0);
    
            @Override
            public Class<?> getColumnClass(int columnIndex) {
                if (columnIndicesWithNumbers.contains(columnIndex))
                    return Double.class;
                return super.getColumnClass(columnIndex);
            }
        }
    }
    

    Preview:

    preview

    After your clarifications in the comment section:

    You can @Override getValueAt method in order to return integers instead of strings.

    Full example:

    public class SorterTest {
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> {
                DefaultTableModel dm = new CustomTableModel();
                dm.addColumn("Integers");
                dm.addColumn("Strings");
                //@formatter:off 
                Object[][] data = { 
                        { "1", "Something" }, { "102", "Something Else" }, 
                        { "55", "Something" }, { "66", "Something" },
                        { "1000", "Something" }, { "1524", "Something" }, 
                        { "5801", "Something" }, { "-55", "Something" },};
                //@formatter:on
                for (Object[] row : data) {
                    dm.addRow(row);
                }
                JTable table = new JTable(dm);
                table.setAutoCreateRowSorter(true);
    
                JOptionPane.showMessageDialog(null, new JScrollPane(table));
            });
        }
    
        public static class CustomTableModel extends DefaultTableModel {
            private final List<Integer> columnIndicesWithNumbers = Arrays.asList(0);
    
            @Override
            public Object getValueAt(int row, int column) {
                Object value = super.getValueAt(row, column);
                if (columnIndicesWithNumbers.contains(column)) {
                    return Double.parseDouble(String.valueOf(value));
                }
                return value;
            }
    
            @Override
            public Class<?> getColumnClass(int columnIndex) {
                if (columnIndicesWithNumbers.contains(columnIndex))
                    return Double.class;
                return super.getColumnClass(columnIndex);
            }
        }
    }