Wie lassen sich Inhalte in einer JTable suchen? v.1.6

Die Klassen TableRowSorter und RowFilter erlauben seit Java 1.6 das Durchsuchen, Sortieren und Filtern einer Tabelle. Siehe auch den Artikel: Tabelle sortieren und filtern.

Im Beispiel wird zur Datenverwaltung zunächst ein einfaches DefaultTableModel model mit fünf Einträgen und einem Header erzeugt. Das Model wird zur Erzeugung der Tabelle anschließend dem Konstruktor einer JTable übergeben. Die dann aufgerufene Methode createRowSorter() erzeugt einen TableRowSorter auf dem TableModel und fügt diesen der Tabelle hinzu. Er ist die Grundlage für die Filterung des Tabellen-Inhaltes.
Im weiteren Verlauf des Konstruktors werden die Tabellenzellen mit einem Rahmen versehen (Achtung! Die Standardrahmenfarbe ist weiß, deshalb muss sie, um sichbar zu werden, explizit gesetzt werden) und zwei Buttons im unteren Teil des Fensters erzeugt, die zur Anzeige des Suchfensters und zum Zurücksetzen des Tabelleninhaltes dienen.
Der Aufruf des Buttons Suche ruft die Methode search() auf. In ihr wird als erstes ein Input-Dialog erzeugt, der neben dem obligatorischen Suchfeld noch eine Checkbox aufweist, mit der zwischen zwei verschiedenen Such- und Anzeigemodi gewählt werden kann.

Checkbox Modus
checked Anzeige nur der selektierten Zeilen
unchecked Anzeige aller Zeilen mit Markierung der selektierten Zeilen

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;

public class Tabellensuche extends JFrame {

    TableRowSorter<DefaultTableModel> sorter;
    final JTable table;

    public Tabellensuche() {
        Object[] header = { "Nr", "Vorname", "Name", "Job" };
        Object[][] data = { { "3", "Karl", "Meier", "Chef" },
                { "2", "Paul", "Schmitz", "Azubi" },
                { "1", "Fritz", "Kunze", "Geselle" },
                { "4", "Heinz", "Hinze", "Meister" },
                { "5", "Fritz", "Brecht", "Hausmeister" } };
        DefaultTableModel model = new DefaultTableModel(data, header);
        table = new JTable(model);

        createRowSorter(model);

        table.setShowGrid(true);
        table.setGridColor(Color.BLACK);
        this.add(new JScrollPane(table), BorderLayout.CENTER);

        JButton searchButt = new JButton("Suche");
        searchButt.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                search();
            }
        });
        JButton resetButt = new JButton("Zur\u00FCcksetzen");
        resetButt.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                createRowSorter((DefaultTableModel) table.getModel());
            }
        });
        JPanel buttPanel = new JPanel(new FlowLayout());
        buttPanel.add(searchButt);
        buttPanel.add(resetButt);
        this.add(buttPanel, BorderLayout.SOUTH);

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(400, 250);
        this.setLocationRelativeTo(null);
        this.setTitle("Tabellensuche");
        this.setVisible(true);
    }

    public void search() {
        JPanel panel = new JPanel(new FlowLayout());
        JCheckBox cb = new JCheckBox("Selektive Suche");
        panel.add(cb);
        String text = JOptionPane.showInputDialog(panel);
        if (cb.isSelected()) {
            // nur gesuchte Zeilen anzeigen
            sorter.setRowFilter(RowFilter.regexFilter(text));
            for (int i = 0; i < table.getRowCount(); i++) {
                for (int j = 0; j < table.getColumnCount(); j++) {
                    System.out.println(table.getValueAt(i, j));
                }
            }
        } else {
            // gesuchte Zeilen markieren
            DefaultTableModel model = (DefaultTableModel) table.getModel();
            for (int i = 0; i < model.getRowCount(); i++) {
                for (int j = 0; j < model.getColumnCount(); j++) {
                    if (model
                            .getValueAt(table.convertRowIndexToModel(i),
                                    table.convertColumnIndexToModel(j))
                            .toString().indexOf(text) != -1) {
                        Rectangle r = table.getCellRect(i, 0, false);
                        table.scrollRectToVisible(r);
                        table.changeSelection(i, j, true, false);
                        for(int k=0; k<table.getColumnCount(); k++){
                            System.out.println(table.getValueAt(i, k));
                        }
                    }
                }
            }
        }
    }

    private void createRowSorter(DefaultTableModel model) {
        sorter = new TableRowSorter<DefaultTableModel>(model);
        table.setRowSorter(sorter);
    }

    public static void main(String[] args) {
        new Tabellensuche();
    }
}

Abhängig vom Auswahlzustand der Checkbox wird zwischen den beiden Modi in einer Verzweigung unterschieden: Ist die Checkbox selektiert, so wird eine Suche nach dem eingetragenen Text als regulärem Ausdruck durchgeführt und die Tabellendarstellung auf die Suchergebnisse beschnitten. Die Ausgabe (oder ggf. Weiterverarbeitung) der Suchergebnisse erfolgt so, dass die verbleibende Tabelle Zelle für Zelle durchlaufen und ausgelesen wird.
Ist die Checkbox nicht selektiert, so erfolgt die Suche etwas anders: Hier wird in einer geschachtelten Schleife das Model durchlaufen. Der Inhalt jeder Tabellenzelle wird hierin gegen den Inhalt des entsprechenden Modeleintrags getestet. Wichtig ist hierbei, dass die Zellenindices durch die Methode convertColumnIndexToModel() konvertiert werden. Der Hintergrund liegt darin begründet, dass z.B. die Zeile 2 des Models nicht unbedingt der Zeile 2 der Tablle entsprechen muss, z.B. nach einer Umsortierung, wie sie durch Klicken auf den Tabellenkopf stattfinden kann.
Von der ersten Zelle der passenden Zeile wird dann dessen Fläche als Rectangle -Objekt ermittelt. Die Methode JComponent.scrollRectToVisible() ermöglicht es dann, zu der gefundenen Position zu scrollen. Die Methode JTable.table.changeSelection() selektiert diese dann. Der Methode werden vier Parameter übergeben: Neben dem Zeilen- und Spaltenindex werden zwei boolean Werte erwartet, deren Kombinationen folgendes ermöglicht:

toggle extend Funktion
false false Lösche die bisherige Selektion und selektiere die neue Zelle
false true Erweitere die bisherige Selektion bis zur angegebenen Zelle und lösche alle anderen Selektionen
true false Kehre die Selektion der angegebenen Zelle ins Gegenteil um
true true Übernimm den Selektionszustand der Ankerzelle zwischen diesem und der angegebenen Zelle

Da die gesuchten Zeilen naturgemäß bislang nicht selektiert sind, werden sie im Beispiel nach der dritten Parameter-Variante selektiert. Zum Abschluss werden die ausgewählten Zeilen Zelle für Zelle durchlaufen und ausgegeben.
Ein mehrfaches Suchen und Markieren ist auf diese Weise übrigens nicht möglich, da die Selektierten Zellen bei einer erneuten Suche, die auch die bisher ausgewählten Zeilen umfasst, deren Selektionszustand wiederum umkehrt, sodass diese nun nicht mehr markiert sind. Dass die Suche dennoch durchgeführt wird, zeigt die erfolgreiche Ausgabe. Im Beispiel lässt sich das prima durch die Suche nach 'Fritz' und 'itz' nachvollziehen.