首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JTable 排序功能完美解決方案:TableSorter 源碼解析與現代替代方案

     編輯:Java 相關知識

Swing `JTable` 本身不支持列排序,Sun 官方 Tutorial 提供了經典 `TableSorter` 實現,通過裝飾器模式包裝 `TableModel`,提供多列排序、箭頭指示、鍵盤/滑鼠交互。本文深度解析其核心原理、完整源碼、使用姿勢,並給出現代 Swing 排序最佳實踐 (`RowSorter` / `TableRowSorter`)。

一、 核心設計:裝飾器模式

  • `TableSorter extends AbstractTableModel`:實現 `TableModel` 接口,對外表現為可排序模型
  • 內部持有 `TableModel tableModel`:委託數據存儲
  • 維護映射數組:
    • `Row[] viewToModel`:視圖行索引 → 模型行索引
    • `int[] modelToView`:模型行索引 → 視圖行索引 (反向查找優化)
  • 排序邏輯:對 `viewToModel` 數組按 `Comparator` 排序,數據不動,只調整映射

二、 關鍵源碼片段解析

1. 視圖模型映射構建

private Row[] getViewToModel() {
    if (viewToModel == null) {
        int n = tableModel.getRowCount();
        viewToModel = new Row[n];
        for (int i = 0; i < n; i++) viewToModel[i] = new Row(i); // 初始恆等映射
        if (isSorting()) Arrays.sort(viewToModel); // 按排序規則排序
    }
    return viewToModel;
}

private int[] getModelToView() {
    if (modelToView == null) {
        int n = getViewToModel().length;
        modelToView = new int[n];
        for (int i = 0; i < n; i++) modelToView[modelIndex(i)] = i; // 反向索引
    }
    return modelToView;
}

2. Row 比較器:多列排序邏輯

private class Row implements Comparable<Row> {
    int modelIndex;
    public int compareTo(Row o) {
        for (Directive d : sortingColumns) { // 遍歷排序列列表
            int col = d.column;
            Object v1 = tableModel.getValueAt(modelIndex, col);
            Object v2 = tableModel.getValueAt(o.modelIndex, col);
            int cmp = compareValues(v1, v2, col); // 空值處理 + Comparator
            if (cmp != 0) return d.direction == DESCENDING ? -cmp : cmp;
        }
        return 0; // 完全相等
    }
}

3. 表頭滑鼠交互:排序狀態機

// MouseHandler.mouseClicked
int status = getSortingStatus(column);
if (!e.isControlDown()) cancelSorting(); // 無 Ctrl 清空其他列
// 狀態循環:無序 → 升序 → 降序 → 無序 (Shift 反向)
status = status + (e.isShiftDown() ? -1 : 1);
status = (status + 4) % 3 - 1; // 有限狀態機 {-1,0,1}
setSortingStatus(column, status);

4. 模型事件響應:增量更新 vs 完全重排

public void tableChanged(TableModelEvent e) {
    if (!isSorting()) { fireTableChanged(e); return; }
    if (e.getFirstRow() == TableModelEvent.HEADER_ROW) { cancelSorting(); fireTableChanged(e); return; }
    // 單元格變更且非排序列且有反向索引 → 增量更新視圖
    if (e.getFirstRow() == e.getLastRow() && column != ALL_COLUMNS && getSortingStatus(column) == NOT_SORTED && modelToView != null) {
        int viewRow = modelToView[e.getFirstRow()];
        fireTableChanged(new TableModelEvent(this, viewRow, viewRow, column, e.getType()));
        return;
    }
    // 其他情況:數據可能破壞排序 → 清空映射,觸發完全重排
    clearSortingState();
    fireTableDataChanged();
}

三、 使用姿勢 (經典 TableSorter)

// 1. 創建模型
MyTableModel model = new MyTableModel();

// 2. 包裝 TableSorter
TableSorter sorter = new TableSorter(model);

// 3. 創建 JTable 使用 sorter
JTable table = new JTable(sorter);

// 4. 關鍵:綁定表頭以啟用滑鼠排序
sorter.setTableHeader(table.getTableHeader());

// 5. 程式化控制排序 (可選)
sorter.setSortingStatus(0, TableSorter.ASCENDING); // 第 0 列升序
sorter.setSortingStatus(1, TableSorter.DESCENDING); // 第 1 列降序 (多列排序)

四、 現代最佳實踐:JDK 6+ RowSorter / TableRowSorter

JDK 6 引入官方排序框架,**無需第三方類**,功能更強、性能更好、支持泛型:

// 一行代碼啟用排序
JTable table = new JTable(model);
table.setAutoCreateRowSorter(true); // 自動創建 TableRowSorter

// 或手動配置高級選項
TableRowSorter<MyTableModel> sorter = new TableRowSorter<>(model);
table.setRowSorter(sorter);

// 程式化排序
sorter.setSortKeys(List.of(
    new RowSorter.SortKey(0, SortOrder.ASCENDING),
    new RowSorter.SortKey(1, SortOrder.DESCENDING)
));

// 自定義比較器
sorter.setComparator(2, (o1, o2) -> {
    // 自定義邏輯,如日期字符串解析比較
    return parseDate(o1.toString()).compareTo(parseDate(o2.toString()));
});

// 過濾器 (RowSorter 獨有功能)
sorter.setRowFilter(RowFilter.regexFilter("keyword", 0)); // 第 0 列正則過濾

五、 TableSorter vs TableRowSorter 對比

特性TableSorter (Sun Tutorial)TableRowSorter (JDK 6+)
依賴需複製 300+ 行源碼零依賴,核心庫內置
泛型支持無完整泛型
多列排序支持 (Ctrl+點擊)支持 (API + UI)
行過濾不支持內置 RowFilter (正則、日期、數值、自定義)
性能基礎優化增量更新、懶加載
維護性自行維護JDK 官方維護
推薦度⚠️ 僅維護舊項目✅ **新項目首選**

六、 遷移指南:從 TableSorter 到 TableRowSorter

  1. 刪除 `TableSorter.java` 源文件
  2. 將 `new JTable(sorter)` 改為 `new JTable(model)` + `table.setAutoCreateRowSorter(true)`
  3. 將 `sorter.setTableHeader(...)` 刪除 (自動綁定)
  4. 程式化排序調用改為 `RowSorter.SortKey` 列表
  5. 利用 `setRowFilter` 實現搜索過濾功能

七、 總結

`TableSorter` 是 Swing 早期排序的經典教學實現,展示了**裝飾器模式、視圖模型映射、多列排序狀態機、表頭交互**等核心思想。但現代開發**應直接使用 `TableRowSorter`**,獲得零依賴、泛型安全、行過濾、官方維護等全方位優勢。理解 `TableSorter` 原理有助於深入掌握 `RowSorter` 內部機制,但在生產代碼中請拋棄手寫排序器。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved