Swing `JTable` 本身不支持列排序,Sun 官方 Tutorial 提供了經典 `TableSorter` 實現,通過裝飾器模式包裝 `TableModel`,提供多列排序、箭頭指示、鍵盤/滑鼠交互。本文深度解析其核心原理、完整源碼、使用姿勢,並給出現代 Swing 排序最佳實踐 (`RowSorter` / `TableRowSorter`)。
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;
}
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; // 完全相等
}
}
// 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);
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();
}
// 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 引入官方排序框架,**無需第三方類**,功能更強、性能更好、支持泛型:
// 一行代碼啟用排序
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 (Sun Tutorial) | TableRowSorter (JDK 6+) |
|---|---|---|
| 依賴 | 需複製 300+ 行源碼 | 零依賴,核心庫內置 |
| 泛型支持 | 無 | 完整泛型 |
| 多列排序 | 支持 (Ctrl+點擊) | 支持 (API + UI) |
| 行過濾 | 不支持 | 內置 RowFilter (正則、日期、數值、自定義) |
| 性能 | 基礎 | 優化增量更新、懶加載 |
| 維護性 | 自行維護 | JDK 官方維護 |
| 推薦度 | ⚠️ 僅維護舊項目 | ✅ **新項目首選** |
`TableSorter` 是 Swing 早期排序的經典教學實現,展示了**裝飾器模式、視圖模型映射、多列排序狀態機、表頭交互**等核心思想。但現代開發**應直接使用 `TableRowSorter`**,獲得零依賴、泛型安全、行過濾、官方維護等全方位優勢。理解 `TableSorter` 原理有助於深入掌握 `RowSorter` 內部機制,但在生產代碼中請拋棄手寫排序器。