TableModel: Wo werden die RowSorter gespeichert?

Hallo, etwas spezielle Frage. Bietet das TableModel eine Methode zum Überschreiben an, um den entsprechenden RowSorter zurückzugeben?

Zurzeit habe ich so etwas, und setze die RowSorter dann auf der JTable, was mir aber unsinnig erscheint, bzw. komplizierter als vielleicht nötig:

  public static class MyTableModel extends AbstractTableModel {
    private final String[] columnNames = {
      "CONTAINER ID",
      "NAME",
      "CPU %",
      "MEM USAGE",
      "LIMIT",
      "MEM %",
      "NET I",
      "NET O",
      "BLOCK I",
      "BLOCK O",
      "PIDS",
      "RUNS"
    };

    private final ArrayList<ArrayList<Object>> data = new ArrayList<>();

    public void update() {
      data.clear();
      String[] tableArray = getTableArray();
      for (String line : tableArray) {
        addRow(line);
      }
      fireTableDataChanged();
    }

    private void addRow(String line) {
      if (line.startsWith("CONTAINER ID")) {
        return;
      }
      String[] rowData = line.split("\\s{2,}");
      String[] temp1 = rowData[3].split(" / ");
      String[] temp2 = rowData[5].split(" / ");
      String[] temp3 = rowData[6].split(" / ");
      ArrayList<Object> row = new ArrayList<>();
      row.add(rowData[0]);
      row.add(rowData[1]);
      row.add(rowData[2]);
      row.add(temp1[0]);
      row.add(temp1[1]);
      row.add(rowData[4]);
      row.add(temp2[0]);
      row.add(temp2[1]);
      row.add(temp3[0]);
      row.add(temp3[1]);
      row.add(rowData[7]);
      row.add("0".equals(rowData[7]) ? "No" : "Yes");
      data.add(row);
    }

    @Override
    public int getRowCount() {
      return data.size();
    }

    @Override
    public int getColumnCount() {
      return columnNames.length;
    }

    @Override
    public String getColumnName(int column) {
      return columnNames[column];
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
      return data.get(rowIndex).get(columnIndex);
    }

    public TableRowSorter<MyTableModel> getSorter() {
      TableRowSorter<MyTableModel> sorter = new TableRowSorter<>(this);
      TreeMap<String, Long> map =
          new TreeMap<>(
              (s1, s2) -> {
                int l1 = s1.length();
                int l2 = s2.length();
                if (l1 != l2) {
                  return Integer.compare(l2, l1);
                }
                return s1.compareTo(s2);
              });
      map.put("%", 1L);
      map.put("B", 1L);
      map.put("KiB", 1024L);
      map.put("MiB", 1024L * 1024L);
      map.put("GiB", 1024L * 1024L * 1024L);
      map.put("kB", 1000L);
      map.put("MB", 1000L * 1000L);
      map.put("GB", 1000L * 1000L * 1000L);
      Comparator<String> comparator =
          (o1, o2) -> {
            Double d1 = null;
            for (Map.Entry<String, Long> entry : map.entrySet()) {
              if (o1.endsWith(entry.getKey())) {
                d1 =
                    Double.parseDouble(o1.substring(0, o1.length() - entry.getKey().length()))
                        * entry.getValue();
                break;
              }
            }
            Double d2 = null;
            for (Map.Entry<String, Long> entry : map.entrySet()) {
              if (o2.endsWith(entry.getKey())) {
                d2 =
                    Double.parseDouble(o2.substring(0, o2.length() - entry.getKey().length()))
                        * entry.getValue();
                break;
              }
            }
            if (d1 == null || d2 == null) {
              return o1.compareTo(o2);
            }
            return Double.compare(d1, d2);
          };
      for (int i = 0; i < columnNames.length; i++) {
        sorter.setComparator(i, comparator);
      }
      return sorter;
    }
  }
    MyTableModel model = new MyTableModel();
    JTable table = new JTable(model);
    table.setRowSorter(model.getSorter());

Ich verstehe nicht, weshalb die RowSorter wohl in der JTable gespeichert werden.

Normalerweise erzeugt man eine Instanz des TableSorter, die ggf. eine separate (innere Klasse) eines speziellen Comparator nutzt und übergibt ihn bei der Instanziierung der JTable.

//im Konstruktor oder einer privaten create()-Methode für GUI-Teile

table = new JTable(tableModel);
TableRowSorter<TableModel> sorter = new TableRowSorter<>(tableModel);
table.setRowSorter(sorter);

//optional, wenn ein Comparator genutzt wird (hier einer, der Zeichen nach einem bestimmten Muster sortiert)
sorter.setComparator(0, new AlphanumericComparator());
List<RowSorter.SortKey> sortKeys = new ArrayList<>();
sortKeys.add(new RowSorter.SortKey(0, SortOrder.ASCENDING));
sortKeys.add(new RowSorter.SortKey(1, SortOrder.DESCENDING));
sorter.setSortKeys(sortKeys);

panel.add(new JScrollPane(table), BorderLayout.CENTER);

So bleibt das Model von einem Sorter getrennt.

Moin @L-ectron-X , bitte verwende doch Java-Tags…

Das mache ich ja oben auch (neuen Sorter anlegen und Comperator hinzufügen), nur bei mir steht das im TableModel, weil ich dachte, das TableModel kennt die zu sortierenden Daten am ehesten.

Ungeachtet dessen… ist ein „Multi-Row-Comparator-Sorter“ falsch? Also sollte es für jede Spalte genau einen „One-Row-Comperator-Sorter“ geben?


Für alle Elemente jeder Spalte sollte eigentlich immer nur erster und zweiter Fall eintreten, niemals beide. … Sonst bin ich mir nicht sicher, ob alle Ordnungseigenschaften eingehalten sind.

Auch auf die Gefahr hin, durch diese Antwort einen logischen Widerspruch zu erzeugen:

Gründe, warum der Sorter nichts im Model verloren hat, sind…

  • die Sortierung in Swing passiert rein view-seitig passiert. Im TableModel liegen die Daten so wie sie sind. Sie werden nur in der JTable in einer anderen Reihenfolge angezeigt
  • die Daten sind „öffentlich zugreifbar“. D.h. es gibt keinen Grund, den Sorter oder einen Comparator Teil des Modells zu machen. Jeder Aspekt, der für die Sortierung relevant ist, kann „von außen“ abgeholt werden.

(Mein pet-peeve beim Swing Table-Sortieren is übrigens dass man die Tabelle nicht un-sortiert machen kann! - aber dafür gibt es zum Glück eine Lösung…)

Den speziellen Comparator-Murx, den du da zusammengemurxt hast, muss ich wohl nicht weiter kommentieren…

@Marco13 Habe mir jetzt einen eigenen RowSorter komplett selber geschrieben, der tut, was ich möchte.

Erstmal die Daten:

Dann der Code:

  public static class MyTableModel extends AbstractTableModel {
    private final String[] columnNames = {
      "CONTAINER ID",
      "NAME",
      "CPU %",
      "MEM USAGE",
      "LIMIT",
      "MEM %",
      "NET I",
      "NET O",
      "BLOCK I",
      "BLOCK O",
      "PIDS",
      "RUNS"
    };

    private final ArrayList<ArrayList<Object>> data = new ArrayList<>();

    public void update() {
      data.clear();
      String[] tableArray = getTableArray();
      for (String line : tableArray) {
        addRow(line);
      }
      fireTableDataChanged();
    }

    private void addRow(String line) {
      if (line.startsWith("CONTAINER ID")) {
        return;
      }
      String[] rowData = line.split("\\s{2,}");
      String[] temp1 = rowData[3].split(" / ");
      String[] temp2 = rowData[5].split(" / ");
      String[] temp3 = rowData[6].split(" / ");
      ArrayList<Object> row = new ArrayList<>();
      row.add(rowData[0]);
      row.add(rowData[1]);
      row.add(rowData[2]);
      row.add(temp1[0]);
      row.add(temp1[1]);
      row.add(rowData[4]);
      row.add(temp2[0]);
      row.add(temp2[1]);
      row.add(temp3[0]);
      row.add(temp3[1]);
      row.add(rowData[7]);
      row.add("0".equals(rowData[7]) ? "No" : "Yes");
      data.add(row);
    }

    @Override
    public int getRowCount() {
      return data.size();
    }

    @Override
    public int getColumnCount() {
      return columnNames.length;
    }

    @Override
    public String getColumnName(int column) {
      return columnNames[column];
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
      return data.get(rowIndex).get(columnIndex);
    }

    public RowSorter<MyTableModel> getSorter() {
      return new RowSorter<>() {
        final ArrayList<SortKey> sortKeys = new ArrayList<>();
        int[] indexesModel = new int[0];
        int[] indexesView = new int[0];
        boolean isSorted = true;

        {
          updateIndexes();
        }

        @Override
        public MyTableModel getModel() {
          return MyTableModel.this;
        }

        @Override
        public void toggleSortOrder(int column) {
          SortKey old = null;
          for (Iterator<SortKey> it = sortKeys.iterator(); it.hasNext(); ) {
            SortKey sortKey = it.next();
            if (sortKey.getColumn() == column) {
              old = sortKey;
              it.remove();
              break;
            }
          }
          if (old == null) {
            sortKeys.add(0, new SortKey(column, SortOrder.ASCENDING));
          } else {
            sortKeys.add(
                0,
                new SortKey(
                    column,
                    old.getSortOrder() == SortOrder.ASCENDING
                        ? SortOrder.DESCENDING
                        : SortOrder.ASCENDING));
          }
          updateIndexes();
        }

        @Override
        public int convertRowIndexToModel(int index) {
          sort();
          return indexesView[index];
        }

        @Override
        public int convertRowIndexToView(int index) {
          sort();
          return indexesModel[index];
        }

        @Override
        public void setSortKeys(List<? extends SortKey> keys) {
          sortKeys.clear();
          sortKeys.addAll(keys);
        }

        @Override
        public List<? extends SortKey> getSortKeys() {
          return new ArrayList<>(sortKeys);
        }

        @Override
        public int getViewRowCount() {
          return getRowCount();
        }

        @Override
        public int getModelRowCount() {
          return getRowCount();
        }

        @Override
        public void modelStructureChanged() {
          updateIndexes();
        }

        @Override
        public void allRowsChanged() {
          updateIndexes();
        }

        @Override
        public void rowsInserted(int firstRow, int endRow) {
          updateIndexes();
        }

        @Override
        public void rowsDeleted(int firstRow, int endRow) {
          updateIndexes();
        }

        @Override
        public void rowsUpdated(int firstRow, int endRow) {
          updateIndexes();
        }

        @Override
        public void rowsUpdated(int firstRow, int endRow, int column) {
          updateIndexes();
        }

        private void updateIndexes() {
          indexesModel = new int[getModelRowCount()];
          indexesView = new int[getModelRowCount()];
          for (int i = 0; i < getModelRowCount(); i++) {
            indexesModel[i] = i;
            indexesView[i] = i;
          }
          isSorted = false;
        }

        private void sort() {
          if (isSorted) {
            return;
          }
          isSorted = true;
          for (SortKey sortKey : sortKeys.reversed()) {
            if (sortKey.getSortOrder() == SortOrder.UNSORTED) {
              continue;
            }
            int column = sortKey.getColumn();
            Object[][] data1 = new Object[getModelRowCount()][2];
            for (int i = 0; i < getModelRowCount(); i++) {
              data1[i][0] = i;
            }
            int type;
            switch (column) {
              case 0, 1, 11 -> {
                for (int i = 0; i < getModelRowCount(); i++) {
                  data1[i][1] = getValueAt(i, column);
                }
                type = 0;
              }
              case 2, 5 -> {
                for (int i = 0; i < getModelRowCount(); i++) {
                  String s = (String) getValueAt(i, column);
                  data1[i][1] = Double.parseDouble(s.substring(0, s.length() - 1));
                }
                type = 1;
              }
              case 3, 4, 6, 7, 8, 9 -> {
                Object[][] temp = {
                  {"KiB", 1024L},
                  {"MiB", 1024L * 1024L},
                  {"GiB", 1024L * 1024L * 1024L},
                  {"kB", 1000L},
                  {"MB", 1000L * 1000L},
                  {"GB", 1000L * 1000L * 1000L},
                  {"B", 1L}
                };
                for (int i = 0; i < getModelRowCount(); i++) {
                  String s = (String) getValueAt(i, column);
                  for (Object[] os : temp) {
                    if (s.endsWith((String) os[0])) {
                      data1[i][1] =
                          Double.parseDouble(s.substring(0, s.length() - ((String) os[0]).length()))
                              * (Long) os[1];
                      break;
                    }
                  }
                }
                type = 1;
              }
              case 10 -> {
                for (int i = 0; i < getModelRowCount(); i++) {
                  data1[i][1] = Integer.parseInt((String) getValueAt(i, column));
                }
                type = 2;
              }
              default -> throw new IllegalStateException("Unexpected column value: " + column);
            }
            int finalType = type;
            Arrays.sort(
                data1,
                (o1, o2) -> {
                  int result;
                  switch (finalType) {
                    case 0 -> result = ((String) o1[1]).compareTo((String) o2[1]);
                    case 1 -> result = Double.compare((double) o1[1], (double) o2[1]);
                    case 2 -> result = Integer.compare((int) o1[1], (int) o2[1]);
                    default -> throw new IllegalStateException();
                  }
                  return sortKey.getSortOrder() == SortOrder.ASCENDING ? result : -result;
                });
            for (int i = 0; i < getModelRowCount(); i++) {
              indexesModel[(int) data1[i][0]] = i;
              indexesView[i] = (int) data1[i][0];
            }
          }
        }
      };
    }
  }

Dann bitte dein Kommentar.

Was leider noch nicht funktioniert ist Folgendes: Erst nach Spalte A sortieren, dann nach Spalte B sortieren. Es wird dann einfach nur nach Spalte B sortiert, aus mir noch nicht erklärlichen Gründen. Aber ich denke, ich hab den RowSorter-Kontrakt eingehalten.

Lass’ mich raten: Er sortiert? :roll_eyes:

Ich werd’ den Code nicht lesen. Wie schon an anderer Stelle viel zu oft gesagt: Der Code, den du hier postest, ist Murks.

Hier ist ein kleines Beispiel, wo an einer Stelle ein Comparator eingefügt wurde, der beliebig gefüllt werden könnte. An einer Stelle, mit einer Zeile, ohne sonstige eigene Klassen…

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

public class TableSortingExample
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(
            () -> createAndShowGUI());
    }

    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        Object data[][] = {
            { "a0", "b0", "c0" },
            { "a0", "b0", "c1" },
            { "a0", "b0", "c2" },
            { "a0", "b1", "c0" },
            { "a0", "b1", "c1" },
            { "a0", "b1", "c2" },
            { "a0", "b2", "c0" },
            { "a0", "b2", "c1" },
            { "a0", "b2", "c2" },
        };
        Object[] columnNames = { "A", "B", "C" };
        TableModel tableModel = new DefaultTableModel(data, columnNames);
        
        JTable table = new JTable(tableModel);
        
        TableRowSorter<TableModel> sorter = 
            new TableRowSorter<TableModel>(tableModel);
        table.setRowSorter(sorter);
        
        sorter.setComparator(2, (v0, v1) ->
        {
            System.out.println("Compare "+v0+" and "+v1);
            return 0;
        });

        f.getContentPane().setLayout(new BorderLayout());
        f.getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

Aber natürlich mache ich das nur, weil ich jemand bin, der zu blöd oder zu faul ist, einen RowSorter selbst zu schreiben, der tut, was ich möchte :roll_eyes:

Ja, der vorherige auch schon. Aber der vorherige speicherte die Daten nicht zwischen.

Es hat geklappt…

  public static class MyTableModel extends AbstractTableModel {
    private final String[] columnNames = {
      "CONTAINER ID",
      "NAME",
      "CPU %",
      "MEM USAGE",
      "LIMIT",
      "MEM %",
      "NET I",
      "NET O",
      "BLOCK I",
      "BLOCK O",
      "PIDS",
      "RUNS"
    };

    private final ArrayList<ArrayList<Object>> data = new ArrayList<>();

    public void update() {
      data.clear();
      String[] tableArray = getTableArray();
      for (String line : tableArray) {
        addRow(line);
      }
      fireTableDataChanged();
    }

    private void addRow(String line) {
      if (line.startsWith("CONTAINER ID")) {
        return;
      }
      String[] rowData = line.split("\\s{2,}");
      String[] temp1 = rowData[3].split(" / ");
      String[] temp2 = rowData[5].split(" / ");
      String[] temp3 = rowData[6].split(" / ");
      ArrayList<Object> row = new ArrayList<>();
      row.add(rowData[0]);
      row.add(rowData[1]);
      row.add(rowData[2]);
      row.add(temp1[0]);
      row.add(temp1[1]);
      row.add(rowData[4]);
      row.add(temp2[0]);
      row.add(temp2[1]);
      row.add(temp3[0]);
      row.add(temp3[1]);
      row.add(rowData[7]);
      row.add("0".equals(rowData[7]) ? "No" : "Yes");
      data.add(row);
    }

    @Override
    public int getRowCount() {
      return data.size();
    }

    @Override
    public int getColumnCount() {
      return columnNames.length;
    }

    @Override
    public String getColumnName(int column) {
      return columnNames[column];
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
      return data.get(rowIndex).get(columnIndex);
    }

    public RowSorter<MyTableModel> getSorter() {
      return new RowSorter<>() {
        final ArrayList<SortKey> sortKeys = new ArrayList<>();
        int[] indexesModel = new int[0];
        int[] indexesView = new int[0];
        boolean isSorted = true;

        {
          updateIndexes();
        }

        @Override
        public MyTableModel getModel() {
          return MyTableModel.this;
        }

        @Override
        public void toggleSortOrder(int column) {
          SortKey old = null;
          for (Iterator<SortKey> it = sortKeys.iterator(); it.hasNext(); ) {
            SortKey sortKey = it.next();
            if (sortKey.getColumn() == column) {
              old = sortKey;
              it.remove();
              break;
            }
          }
          if (old == null) {
            sortKeys.add(0, new SortKey(column, SortOrder.ASCENDING));
          } else {
            sortKeys.add(
                0,
                new SortKey(
                    column,
                    old.getSortOrder() == SortOrder.ASCENDING
                        ? SortOrder.DESCENDING
                        : SortOrder.ASCENDING));
          }
          updateIndexes();
        }

        @Override
        public int convertRowIndexToModel(int index) {
          sort();
          return indexesView[index];
        }

        @Override
        public int convertRowIndexToView(int index) {
          sort();
          return indexesModel[index];
        }

        @Override
        public void setSortKeys(List<? extends SortKey> keys) {
          sortKeys.clear();
          sortKeys.addAll(keys);
        }

        @Override
        public List<? extends SortKey> getSortKeys() {
          return new ArrayList<>(sortKeys);
        }

        @Override
        public int getViewRowCount() {
          return getRowCount();
        }

        @Override
        public int getModelRowCount() {
          return getRowCount();
        }

        @Override
        public void modelStructureChanged() {
          updateIndexes();
        }

        @Override
        public void allRowsChanged() {
          updateIndexes();
        }

        @Override
        public void rowsInserted(int firstRow, int endRow) {
          updateIndexes();
        }

        @Override
        public void rowsDeleted(int firstRow, int endRow) {
          updateIndexes();
        }

        @Override
        public void rowsUpdated(int firstRow, int endRow) {
          updateIndexes();
        }

        @Override
        public void rowsUpdated(int firstRow, int endRow, int column) {
          updateIndexes();
        }

        private void updateIndexes() {
          if (indexesModel.length != getModelRowCount()
              || indexesView.length != getModelRowCount()) {
            indexesModel = new int[getModelRowCount()];
            indexesView = new int[getModelRowCount()];
            for (int i = 0; i < getModelRowCount(); i++) {
              indexesModel[i] = i;
              indexesView[i] = i;
            }
          }
          isSorted = false;
        }

        private void sort() {
          if (isSorted) {
            return;
          }
          isSorted = true;
          for (SortKey sortKey : sortKeys.reversed()) {
            if (sortKey.getSortOrder() == SortOrder.UNSORTED) {
              continue;
            }
            int column = sortKey.getColumn();
            Object[][] data1 = new Object[getModelRowCount()][2];
            for (int i = 0; i < getModelRowCount(); i++) {
              data1[convertRowIndexToView(i)][0] = i;
            }
            int type;
            switch (column) {
              case 0, 1, 11 -> {
                for (int i = 0; i < getModelRowCount(); i++) {
                  data1[convertRowIndexToView(i)][1] = getValueAt(i, column);
                }
                type = 0;
              }
              case 2, 5 -> {
                for (int i = 0; i < getModelRowCount(); i++) {
                  String s = (String) getValueAt(i, column);
                  data1[convertRowIndexToView(i)][1] =
                      Double.parseDouble(s.substring(0, s.length() - 1));
                }
                type = 1;
              }
              case 3, 4, 6, 7, 8, 9 -> {
                Object[][] temp = {
                  {"KiB", 1024L},
                  {"MiB", 1024L * 1024L},
                  {"GiB", 1024L * 1024L * 1024L},
                  {"kB", 1000L},
                  {"MB", 1000L * 1000L},
                  {"GB", 1000L * 1000L * 1000L},
                  {"B", 1L}
                };
                for (int i = 0; i < getModelRowCount(); i++) {
                  String s = (String) getValueAt(i, column);
                  for (Object[] os : temp) {
                    if (s.endsWith((String) os[0])) {
                      data1[convertRowIndexToView(i)][1] =
                          Double.parseDouble(s.substring(0, s.length() - ((String) os[0]).length()))
                              * (Long) os[1];
                      break;
                    }
                  }
                }
                type = 1;
              }
              case 10 -> {
                for (int i = 0; i < getModelRowCount(); i++) {
                  data1[convertRowIndexToView(i)][1] =
                      Integer.parseInt((String) getValueAt(i, column));
                }
                type = 2;
              }
              default -> throw new IllegalStateException("Unexpected column value: " + column);
            }
            int finalType = type;
            Arrays.sort(
                data1,
                (o1, o2) -> {
                  int result;
                  switch (finalType) {
                    case 0 -> result = ((String) o1[1]).compareTo((String) o2[1]);
                    case 1 -> result = Double.compare((double) o1[1], (double) o2[1]);
                    case 2 -> result = Integer.compare((int) o1[1], (int) o2[1]);
                    default -> throw new IllegalStateException();
                  }
                  return sortKey.getSortOrder() == SortOrder.ASCENDING ? result : -result;
                });
            for (int i = 0; i < getModelRowCount(); i++) {
              indexesModel[(int) data1[i][0]] = i;
              indexesView[i] = (int) data1[i][0];
            }
          }
        }
      };
    }
  }

Jetzt wird die Sortierungshistorie beachtet.

lost

Danke für den Versuch der Vararschung, aber dein Sorter erfüllt nicht die Anforderungen.

Doch. Er sortiert.

Bei jedem anderen würde ich annehmen, dass der letzte Code, den du jetzt gepostet hast, eine Art Witz sein soll. Bei dir würde ich noch die Möglichkeit des Trollings in Betracht ziehen. Aber da sehe ich einen Widerspruch: Ich glaube nicht, dass das ein Trollversuch war. Ich glaube, dass du wirklich überzeugt bist von dem, was du da machst. Und ein wichtiger Punkt beim Trollen ist, Leute glauben zu lassen, man wäre kein Troll. D.h. wenn das Trolling wäre, dann wärst du „gut“ darin. Aber nach allem, was du so abgibst, weigere ich mich einfach, zu glauben, dass es irgendwas gibt, worin du „gut“ bist. Naja. Außer halt ‚Leuten auf die Nerven gehen‘ und so.

1 „Gefällt mir“

Nein, nicht nach den gewünschten Kriterien.

Ich wollte einfach nur ein Review, keine „Diffamierung“…

Und was sind die Kriterien?

Irgendjemand schrieb mal „Löse dich von diesen Zweitsemester denken“

  1. ich denke er hat dich überschätzt.
  2. Wäre das mein Vorgesetzter, dann würde ich das tun.

Im Übrigen was Marco schrieb.

Nicht nur JF, jetzt fällst du hier auch noch negativ auf… :see_no_evil:

Geh doch woanders trollen. Ich brauche nicht erwähnen, dass das zu kompliziert für dich ist.

Ich könnte jetzt ein „Review“ durchführen. Dazu müßte ich auch wissen, was die Anforderungen sind. Aber da du auch nachdem du dich >20 Jahre in Foren rumreibst immernoch nicht weißt, wie man eine Frage in einem Forum richtig stellt, kennt die halt niemand. Und wenn ich nur den Code reviewen solle, den du hier zu kontextarm reingedumpt hast, dann würde ich mit einer Liste von Punkten anfangen, die dir praktisch jeder bei praktisch jedem Codeschnipsel, den du hier gepostet hast, gesagt hat, und die du immer wieder ignoriert hast.

Es ist zwar etwas paradox, dass ich jetzt diese Frage stelle, aber… : Warum genau sollte ich das tun?

@Marco13 Schon gut. Ich möchte keine extra Arbeit verursachen.

Falls du doch reviewen möchtest, würde ich alle Anforderungen noch einmal aufschreiben und habe auch ein SSCCE (mit Beispieldaten) erstellt. Aber … der Code funktioniert, nur vielleicht habe ich etwas übersehen? Your decision.

Jup. Der Code ist kagge. Für die erste Liste von Gründen, schau einfach auf meine erste Antwort in jedem einzlenen Thread, in dem du nach einem „Review“ gefragt hast. (Zumindest vor dem Zeitpunkt, wo meine erste Antwort wurde: „Schau’ dir an, was ich beim letzten Review gerschrieben habe“ :roll_eyes: )

Hier ist ein complete SSCCE + Beispieldaten (es gibt kleinere Abweichungen):

import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;

public class Main {
  public static void main(String[] args) {
    SwingUtilities.invokeLater(Main::showExample);
  }

  public static class MyTableModel extends AbstractTableModel {
    private final String[] columnNames = {
      "CONTAINER ID",
      "NAME",
      "CPU %",
      "MEM USAGE",
      "LIMIT",
      "MEM %",
      "NET I",
      "NET O",
      "BLOCK I",
      "BLOCK O",
      "PIDS",
      "RUNS"
    };

    private final ArrayList<ArrayList<Object>> data = new ArrayList<>();

    private String[] getExampleTableArray() {
      String s =
          """
              CONTAINER ID   NAME   CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O        PIDS
              afc46a09386c   a      1.72%     885.3MiB / 62.59GiB   1.38%     1.1kB / 426B      4.1kB / 12.9MB   122
              4667f01cc4e1   b      0.00%     164.8MiB / 62.59GiB   0.26%     273kB / 401kB     733kB / 4.1kB    19
              80895db74ac1   c      0.00%     20.88MiB / 62.59GiB   0.03%     5.72MB / 22.8MB   65.5kB / 0B      14
              aabc86e6ff80   d      0.0%      0B / 0B               0.0%      0B / 0B           0B / 0B          0
              e768d957d5e9   e      1.64%     739.6MiB / 62.59GiB   1.15%     106MB / 8.19MB    0B / 52.4GB      21
              4d7eec80628c   f      0.18%     71.52MiB / 62.59GiB   0.11%     10.2kB / 126B     0B / 132MB       29
              29fd9002b8bc   g      0.17%     73.21MiB / 62.59GiB   0.11%     10.7kB / 126B     0B / 146MB       29
              8f21bf47dbfa   h      0.11%     1.264GiB / 62.59GiB   2.02%     1.01MB / 124MB    36.5MB / 176MB   30
              d946feff25d3   i      0.41%     197.5MiB / 62.59GiB   0.31%     3.27MB / 6.19MB   0B / 283MB       42
              ebe628115714   j      0.00%     3.512MiB / 62.59GiB   0.01%     10.6kB / 126B     0B / 659kB       13
              3a9e2b6f2f6d   k      0.07%     23.01MiB / 62.59GiB   0.04%     135MB / 147MB     0B / 4.93MB      18
              ee4278a29fcf   l      0.01%     11.98MiB / 62.59GiB   0.02%     10.2kB / 126B     0B / 73.7kB      13
              df9ea4975405   m      0.01%     24.37MiB / 62.59GiB   0.04%     18.4kB / 5.95kB   0B / 4.1kB       82
              49c5478e30b0   n      0.01%     9.656MiB / 62.59GiB   0.02%     11.2kB / 126B     0B / 4.1kB       6
              b5384fd979ca   o      0.00%     30.06MiB / 62.59GiB   0.05%     10.7kB / 126B     0B / 4.1kB       82
              209c62d6d05d   p      0.00%     74.25MiB / 62.59GiB   0.12%     174MB / 180MB     1.07MB / 0B      14
              """; // The output of "docker stats -a --no-stream | sort -k 2"
      return s.split("\n");
    }

    public void update() {
      data.clear();
      String[] tableArray = getExampleTableArray();
      for (String line : tableArray) {
        addRow(line);
      }
      fireTableDataChanged();
    }

    private void addRow(String line) {
      if (line.startsWith("CONTAINER ID")) {
        return;
      }
      String[] rowData = line.split("\\s{2,}");
      String[] temp1 = rowData[3].split(" / ");
      String[] temp2 = rowData[5].split(" / ");
      String[] temp3 = rowData[6].split(" / ");
      ArrayList<Object> row = new ArrayList<>();
      row.add(rowData[0]);
      row.add(rowData[1]);
      row.add(rowData[2]);
      row.add(temp1[0]);
      row.add(temp1[1]);
      row.add(rowData[4]);
      row.add(temp2[0]);
      row.add(temp2[1]);
      row.add(temp3[0]);
      row.add(temp3[1]);
      row.add(rowData[7]);
      row.add("0".equals(rowData[7]) ? "No" : "Yes");
      data.add(row);
    }

    @Override
    public int getRowCount() {
      return data.size();
    }

    @Override
    public int getColumnCount() {
      return columnNames.length;
    }

    @Override
    public String getColumnName(int column) {
      return columnNames[column];
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
      return data.get(rowIndex).get(columnIndex);
    }

    public RowSorter<MyTableModel> getSorter() {
      return new RowSorter<>() {
        final LinkedHashMap<Integer, SortKey> sortKeys = new LinkedHashMap<>();
        int[] indexesModel;
        int[] indexesView;
        Object[][] data1;
        boolean isSorted = true;

        {
          updateIndexes();
        }

        @Override
        public MyTableModel getModel() {
          return MyTableModel.this;
        }

        @Override
        public void toggleSortOrder(int column) {
          sortKeys.computeIfAbsent(column, k -> new SortKey(column, SortOrder.DESCENDING));
          sortKeys.putFirst(
              column,
              new SortKey(
                  column,
                  sortKeys.get(column).getSortOrder() == SortOrder.ASCENDING
                      ? SortOrder.DESCENDING
                      : SortOrder.ASCENDING));
          updateIndexes();
        }

        @Override
        public int convertRowIndexToModel(int index) {
          sort();
          return indexesView[index];
        }

        @Override
        public int convertRowIndexToView(int index) {
          sort();
          return indexesModel[index];
        }

        @Override
        public void setSortKeys(List<? extends SortKey> keys) {
          sortKeys.clear();
          for (SortKey key : keys) {
            sortKeys.put(key.getColumn(), key);
          }
        }

        @Override
        public List<? extends SortKey> getSortKeys() {
          return new ArrayList<>(sortKeys.values());
        }

        @Override
        public int getViewRowCount() {
          return getRowCount();
        }

        @Override
        public int getModelRowCount() {
          return getRowCount();
        }

        @Override
        public void modelStructureChanged() {
          updateIndexes();
        }

        @Override
        public void allRowsChanged() {
          updateIndexes();
        }

        @Override
        public void rowsInserted(int firstRow, int endRow) {
          updateIndexes();
        }

        @Override
        public void rowsDeleted(int firstRow, int endRow) {
          updateIndexes();
        }

        @Override
        public void rowsUpdated(int firstRow, int endRow) {
          updateIndexes();
        }

        @Override
        public void rowsUpdated(int firstRow, int endRow, int column) {
          updateIndexes();
        }

        private void updateIndexes() {
          indexesModel = new int[getModelRowCount()];
          indexesView = new int[getModelRowCount()];
          data1 = new Object[getModelRowCount()][2];
          for (int i = 0; i < getModelRowCount(); i++) {
            indexesModel[i] = i;
            indexesView[i] = i;
            data1[i][0] = i;
          }
          isSorted = false;
        }

        private void sort() {
          if (isSorted) {
            return;
          }
          isSorted = true;
          Object[] values = sortKeys.values().toArray();
          for (int j = values.length - 1; j >= 0; j--) {
            SortKey sortKey = (SortKey) values[j];
            if (sortKey.getSortOrder() == SortOrder.UNSORTED) {
              continue;
            }
            int column = sortKey.getColumn();
            int type;
            switch (column) {
              case 0, 1, 11 -> {
                for (int i = 0; i < getModelRowCount(); i++) {
                  data1[convertRowIndexToView(i)][1] = getValueAt(i, column);
                }
                type = 0;
              }
              case 2, 5 -> {
                for (int i = 0; i < getModelRowCount(); i++) {
                  String s = (String) getValueAt(i, column);
                  data1[convertRowIndexToView(i)][1] =
                      Double.parseDouble(s.substring(0, s.length() - 1));
                }
                type = 1;
              }
              case 3, 4, 6, 7, 8, 9 -> {
                Object[][] temp = {
                  {"KiB", 1024L},
                  {"MiB", 1024L * 1024L},
                  {"GiB", 1024L * 1024L * 1024L},
                  {"kB", 1000L},
                  {"MB", 1000L * 1000L},
                  {"GB", 1000L * 1000L * 1000L},
                  {"B", 1L}
                };
                for (int i = 0; i < getModelRowCount(); i++) {
                  String s = (String) getValueAt(i, column);
                  for (Object[] os : temp) {
                    if (s.endsWith((String) os[0])) {
                      data1[convertRowIndexToView(i)][1] =
                          Double.parseDouble(s.substring(0, s.length() - ((String) os[0]).length()))
                              * (Long) os[1];
                      break;
                    }
                  }
                }
                type = 1;
              }
              case 10 -> {
                for (int i = 0; i < getModelRowCount(); i++) {
                  data1[convertRowIndexToView(i)][1] =
                      Integer.parseInt((String) getValueAt(i, column));
                }
                type = 2;
              }
              default -> throw new IllegalStateException("Unexpected column value: " + column);
            }
            int finalType = 2 * type + (sortKey.getSortOrder() == SortOrder.DESCENDING ? 1 : 0);
            switch (finalType) {
              case 0 -> Arrays.sort(data1, Comparator.comparing(o -> (String) o[1]));
              case 1 ->
                  Arrays.sort(
                      data1, Comparator.comparing(o -> (String) o[1], Comparator.reverseOrder()));
              case 2 -> Arrays.sort(data1, Comparator.comparing(o -> (Double) o[1]));
              case 3 ->
                  Arrays.sort(
                      data1, Comparator.comparing(o -> (Double) o[1], Comparator.reverseOrder()));
              case 4 -> Arrays.sort(data1, Comparator.comparing(o -> (Integer) o[1]));
              case 5 ->
                  Arrays.sort(
                      data1, Comparator.comparing(o -> (Integer) o[1], Comparator.reverseOrder()));
              default -> throw new IllegalStateException();
            }
            for (int i = 0; i < getModelRowCount(); i++) {
              indexesModel[(int) data1[i][0]] = i;
              indexesView[i] = (int) data1[i][0];
            }
          }
        }
      };
    }
  }

  private static void showExample() {
    MyTableModel model = new MyTableModel();
    JTable table = new JTable(model);
    table.setRowSorter(model.getSorter());
    JFrame frame = new JFrame("Docker Stats");
    frame.add(new JScrollPane(table));
    frame.setSize(1200, 600);
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.setVisible(true);
    model.update();
  }
}

Und es gibt nur zwei Anforderungen:

  • Es soll nach einer beliebigen Spalte sortiert werden können, dabei sollen die tatsächlichen Werte in Byte genommen werden, und für die Sortierung zwischengespeichert werden.
  • Es soll möglich sein, zum Beispiel nach den Spalten A, D und C in dieser Reihenfolge zu sortieren, sprich die Historie soll erhalten bleiben.

Ach ja, und putFirst geht möglicherweise nur mit dem neusten JDK … hier ein Workaround:

        @Override
        public void toggleSortOrder(int column) {
          SortKey old = sortKeys.get(column);
          if (old == null) {
            old = new SortKey(column, SortOrder.DESCENDING);
          } else {
            sortKeys.remove(column);
          }

          LinkedHashMap<Integer, SortKey> temp = new LinkedHashMap<>(sortKeys);
          sortKeys.clear();
          sortKeys.put(
              column,
              new SortKey(
                  column,
                  old.getSortOrder() == SortOrder.ASCENDING
                      ? SortOrder.DESCENDING
                      : SortOrder.ASCENDING));
          sortKeys.putAll(temp);

          updateIndexes();
        }

Was sagen denn deine Kommilitonen dazu?

Die sagen, die Biberplage hätte zugenommen und dass es höchste Zeit sei, dagegen vorzugehen. :biohazard: