Löschen von Tabellendaten

Das Löschen der Daten einer Tabelle geschieht nicht in der Tabelle selbst, sondern in deren Datenverwaltungsstruktur, dem sog. Model.

Tabelle und Model

Die Datenverwaltung einer JTable erfolgt innerhalb eines sog. Models. Es stellt eine Datenverwaltungsstruktur dar, die alle Möglichkeiten kapselt, die Tabellendaten zu erweitern, zu löschen, etc. Dies beinhaltet insbesondere auch alle dort registrierten Listener, etwa zur Aktualisierung der Tabellenansicht bei Änderungen. Die JTable selbst, stellt lediglich die optische Aufarbeitung der Daten, die 'View' dar.
Java definiert als Model die abstrakte Klasse AbstractTableModel, die das Interface TableModel implementiert, und davon abgeleitet, die Klasse DefaultTableModel. Je nach Bedarf können beide Klassen erweitert und deren Methoden überschrieben werden. In einfachen Fällen ist dies jedoch nicht notwendig.

Das Beispiel

Es zeigt eine einfache Tabelle mit vier Spalten und vier Reihen, sowie ebenfalls vier Buttons, die die hier besprochenen Löschfunktionen durch Aufruf jeweils einer gesonderten Methode ausführen.

private JTable createTable() {
        Object[][] data = new Object[][] { 
            { "Ardie", "SV 500", "500", "1930" },
            { "Horex", "Regina Sport", "342", "1952" },
            { "BMW", "R75/5", "750", "1969" },
            { "Triumph", "BDG 250 H", "250", "1952" }
        };
        Object[] header = new Object[] { "Marke", "Typ", "Hubraum", "Baujahr" };
        DefaultTableModel model = new DefaultTableModel(data, header);
        JTable table = new JTable(model);
        table.setAutoCreateRowSorter(true);
        return table;
    }
				

Die Tabelle wird der Übersichtlichkeit halber in einer gesonderten Methode createTable() erzeugt. Zunächst wird dort ein zweidimensionales Array mit String-Daten initialisiert [2-7]. Sie stellen den Tabelleninhalt dar, wobei die inneren Arrays für jeweils eine Tabellenzeile stehen. Die Spaltentitel der Tabelle müssen in einem gesonderten Array abgelegt werden [8]. Sie dienen zur Identifizierung der Spalten.
Beide Arrays werden als Parameter dem Konstruktor bei der Bildung eines DefaultTableModel übergeben [9]. Der Konstruktor ist sechsfach überladen; insbesondere können Daten und Header auch jeweils als Vector übergeben werden. Das dergestalt erzeugte Model dient schließlich als Parameter bei der Erzeugung der JTable [10].

Das Beispielprogramm zum Löschen von Tabellendaten

Daten löschen

Wie oben bereits angedeutet muss beim Löschen von Tabellendaten darauf geachtet werden, dass nicht nur deren optische Präsenz in der Tabelle entfernt wird, sondern vielmehr das Löschen auf Ebene des Models erfolgt. Zur Überprüfung des jeweiligen Ergebnisses werden hier nach der Löschung alle Werte des Model auf der Konsole ausgegeben.
Die Methode printModel() ermittelt hierzu mittels getRowCount() die Zeilenanzahl des Models und durch getColumnCount() die Zahl der Spalten. Sie durchläuft diese innerhalb zweier verschachtelter Schleifen und gibt die Werte aus. Hierzu wird die Model-eigene Methode getValueAt() bei Parametrisierung mit den jeweiligen Zählvariablen verwendet.

private void printModel(TableModel model) {
    for (int i = 0; i < model.getRowCount(); i++) {
        for (int j = 0; j < model.getColumnCount(); j++) {
            System.out.print(model.getValueAt(i, j) + "\t");
        }
        System.out.println();
    }
}

Löschen von einzelnen Tabellenzellen

Zur Ermittlung eines einzelnen Zelleninhaltes kann durch die JTable-Methoden getSelectedRow() und getSelectedColumn() am Schnittpunkt der selektierten Zeile und Spalte die Zelle ermittelt werden. Da die Indizes des Modeleintrags bei Erzeugung mit denjenigen der Tabelle identisch sind, wird dann durch die Modelmethode setValueAt() der Wert auf null gesetzt. Durch Verwenden dieser Methode wird sichergestellt, dass gleichzeitig alle in Frage kommenden, intern verwendeten EventListener benachrichtigt werden und somit auch die Tabellenview entsprechend geändert wird.

Was geschieht jedoch, wenn die Tabelleneinträge umsortiert wurden, etwa durch Klick auf den Spaltenkopf und somit View und Model nicht mehr übereinstimmen? Für diesen Fall wird die Ermittlung der Reihen- und Spaltenposition über die Methoden convertRowIndexToModel() bzw. convertColumnIndexToModel() ausgeführt. Diese, in JTable definierten Methoden konvertieren die Indices entsprechend. Beide Methoden haben zudem mit convertRowIndexToView() und convertColumnIndexToView() auch Entsprechungen für ein umgekehrtes Vorgehen.

private void deleteCell(JTable table) {
    TableModel model = table.getModel();
    int y = table.convertRowIndexToModel(table.getSelectedRow());
    int x = table.convertColumnIndexToModel(table.getSelectedColumn());
    if (x > -1 && y > -1)
        model.setValueAt(null, y, x);
    printModel(model);
}

Löschen einer Zeile

Für das Löschen einer Zeile stellt DefaultTableModel die Methode removeRow() bereit. Sie benötigt den Index der jeweiligen Zeile, der wiederum durch getSelectedRow() ermittelt werden kann. Dies funktioniert übrigens auch, wenn bei der Konfiguration der Tabelle mittels table.setCellSelectionEnabled(true) statt einer Reihe nur eine einzelne Zelle ausgewählt wurde.

private void deleteRow(JTable table) {
    TableModel model = table.getModel();
    int y = table.convertRowIndexToModel(table.getSelectedRow());
    ((DefaultTableModel) model).removeRow(y);
    printModel(model);
}

Löschen einer Spalte

Das Löschen einer Tabellenspalte verhält sich etwas anders als das bisher Gezeigte. Zwar ist in JTable eine Methode removeColumn(), der ein TableColumn-Objekt übergeben wird, deklariert. Sie bewirkt jedoch nur, dass die entsprechende Spalte aus der Tabellen-View entfernt wird. Die Daten bleiben im Model erhalten, wie eine entsprechende Ausgabe mit printModel() beweist. Sie kann jederzeit, etwa durch Neuladen des Models, wieder sichtbar gemacht werden.
removeColumn() erwartet ein TableColumn-Objekt als Parameter, das nicht direkt über den Index ermittelt werden kann. Hierzu muss vielmehr ein Umweg genommen werden: Das benötigte Column-Objekt wird durch die Methode getColumn() beschafft. Sie nimmt den Spaltentitel als Parameter entgegen, der wiederum mittels getColumnName() über den Index der selektierten Spalte ermittelt werden kann.

private void deleteCol(JTable table) {
    TableModel model = table.getModel();
    int x = table.convertColumnIndexToModel(table.getSelectedColumn());
    table.removeColumn(table.getColumn(table.getColumnName(x)));
    printModel(model);
}

Um eine Spalte auch aus dem Model zu entfernen, bietet es sich an, ein neues Model durch partielles Kopieren zu erzeugen und anschließend der Tabelle zu übergeben. Die Methode deleteColFromModel() demonstriert hierzu ein mögliches Vorgehen.

Dies geschieht hier dadurch, dass das bisherige Model durchlaufen wird und die Daten in ein neues kopiert werden. Die Daten der zu entfernenden Spalte werden dabei übersprungen.
Hierzu wird für jede Zeile ein Vector-Objekt, rowVector, erzeugt [7], in das die Daten kopiert werden [18] und das dann in der äußeren Schleife dem Gesamtvector, dataVector, hinzugefügt wird [20]. Ein weiterer Vector, colIdent, speichert die verbleibenden Spaltennamen, die beim Durchlaufen der ersten Zeile durch die Modelmethode getColumnName() ermittelt werden [16]. Die Variable x, die den Index der ausgewählten Spalte speichert, wird bei jedem Durchlauf einer neuen Zeile mit der inneren Zählvariablen der Spalte verglichen [12]. Sind sie identisch wird der Kopiervorgang durch continue unterbrochen [13] und auf diese Weise die ausgewählte Spalte übersprungen.
Das neue Model wird schließlich auf die bekannte Art erzeugt [22] und der Tabelle übergeben [23]. Um die Änderung der Tabellenstruktur anzustoßen, muss in einem letzten Schritt die Methode fireTableStructureChanged() aufgerufen werden [24].

private void deleteColFromModel(JTable table) {
    TableModel model = table.getModel();
    int rows = model.getRowCount();
    int cols = model.getColumnCount();
    Vector<Vector<Object>> dataVector = new Vector<Vector<Object>>();
    Vector<Object> colIdent = new Vector<Object>();
    Vector<Object> rowVector;
    int x = table.convertColumnIndexToModel(table.getSelectedColumn());
    for (int i = 0; i < rows; i++) {
        rowVector = new Vector<Object>();
        for (int j = 0; j < cols; j++) {
            if (x == j) {
                continue;
            }
            if (i == 0) {
                colIdent.add(model.getColumnName(j));
            }
            rowVector.add(model.getValueAt(i, j));
        }
        dataVector.add(rowVector);
    }
    DefaultTableModel newModel = new DefaultTableModel(dataVector, colIdent);
    table.setModel(newModel);
    newModel.fireTableStructureChanged();
    printModel(newModel);
}

Vollständiger Quelltext

package swing.tabelle;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.Vector;

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

public class DeleteTableValues {

    public DeleteTableValues() {
        JFrame frame = new JFrame();
        initGui(frame);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setTitle("Tabellen-Werte l\u00f6schen");
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void initGui(JFrame frame) {
        frame.setLayout(new BorderLayout());
        JTable table = createTable();
        JScrollPane sp = new JScrollPane(table);
        sp.setPreferredSize(new Dimension(sp.getPreferredSize().width, 100));
        frame.add(sp, BorderLayout.CENTER);
        JButton cellDelButt = new JButton("Zelle löschen");
        JButton rowDelButt = new JButton("Reihe löschen");
        JButton colDelButt = new JButton("Spalte löschen");
        JButton modelColDelButt = new JButton("Spalte in Model löschen");
        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new GridLayout(1, 3));
        buttonPanel.add(cellDelButt);
        cellDelButt.addActionListener(e -> deleteCell(table));
        buttonPanel.add(rowDelButt);
        rowDelButt.addActionListener(e -> deleteRow(table));
        buttonPanel.add(colDelButt);
        colDelButt.addActionListener(e -> deleteCol(table));
        buttonPanel.add(modelColDelButt);
        modelColDelButt.addActionListener(e -> deleteColFromModel(table));
        frame.add(buttonPanel, BorderLayout.SOUTH);
    }

    private JTable createTable() {
        Object[][] data = new Object[][] { { "Ardie", "SV 500", "500", "1930" },
                { "Horex", "Regina Sport", "342", "1952" }, { "BMW", "R75/5", "750", "1969" },
                { "Triumph", "BDG 250 H", "250", "1952" } };
        Object[] header = new Object[] { "Marke", "Typ", "Hubraum", "Baujahr" };
        DefaultTableModel model = new DefaultTableModel(data, header);
        JTable table = new JTable(model);
        table.setAutoCreateRowSorter(true);
        return table;
    }

    private void deleteCell(JTable table) {
        TableModel model = table.getModel();
        int y = table.convertRowIndexToModel(table.getSelectedRow());
        int x = table.convertColumnIndexToModel(table.getSelectedColumn());
        if (x > -1 && y > -1)
            model.setValueAt(null, y, x);
        printModel(model);
    }

    private void deleteRow(JTable table) {
        TableModel model = table.getModel();
        int y = table.convertRowIndexToModel(table.getSelectedRow());
        ((DefaultTableModel) model).removeRow(y);
        printModel(model);
    }

    private void deleteCol(JTable table) {
        TableModel model = table.getModel();
        int x = table.convertColumnIndexToModel(table.getSelectedColumn());
        table.removeColumn(table.getColumn(table.getColumnName(x)));
        printModel(model);
    }

    private void deleteColFromModel(JTable table) {
        TableModel model = table.getModel();
        int rows = model.getRowCount();
        int cols = model.getColumnCount();
        Vector<Vector<Object>> dataVector = new Vector<Vector<Object>>();
        Vector<Object> colIdent = new Vector<Object>();
        Vector<Object> rowVector;
        int x = table.convertColumnIndexToModel(table.getSelectedColumn());
        for (int i = 0; i < rows; i++) {
            rowVector = new Vector<Object>();
            for (int j = 0; j < cols; j++) {
                if (x == j) {
                    continue;
                }
                if (i == 0) {
                    colIdent.add(model.getColumnName(j));
                }
                rowVector.add(model.getValueAt(i, j));
            }
            dataVector.add(rowVector);
        }
        DefaultTableModel newModel = new DefaultTableModel(dataVector, colIdent);
        table.setModel(newModel);
        newModel.fireTableStructureChanged();
        printModel(newModel);
    }

    private void printModel(TableModel model) {
        for (int i = 0; i < model.getRowCount(); i++) {
            for (int j = 0; j < model.getColumnCount(); j++) {
                System.out.print(model.getValueAt(i, j) + "\t");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new DeleteTableValues());
    }
}

Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.