JavaFX中TableView检测到重复TableColumns异常求助
错误原因分析
你遇到的java.lang.IllegalStateException: Duplicate TableColumns detected错误,核心问题是同一个TableView的列列表被重复添加了相同的TableColumn实例。
看你的代码逻辑,每次点击按钮触发setTable方法时,都会执行:
controller.table1.getColumns().addAll(controller.C1,controller.N1,controller.S1);
如果这个方法被多次调用(比如重复点击按钮),就会把同一组列反复添加到TableView的columns列表中,直接触发重复列的报错。
另外还有一个隐藏问题:你每次调用setTable时都通过FXMLLoader重新加载table_scene.fxml,这会创建全新的控制器和UI组件实例——如果你的table_scene场景已经在界面上显示,那你操作的其实是一个未显示的新实例,不仅会导致重复列,还会让数据无法正确显示到用户看到的表格上。
具体修复方案
1. 避免重复添加列
在添加列之前,先清空TableView的列列表,或者检查列是否已存在再添加:
// 方案1:清空现有列后再添加新列 controller.table1.getColumns().clear(); controller.table1.getColumns().addAll(controller.C1,controller.N1,controller.S1);
如果不想清空所有列(比如表格有固定列),可以先检查列是否已存在:
// 方案2:仅当列不存在时才添加 if (!controller.table1.getColumns().contains(controller.C1)) { controller.table1.getColumns().addAll(controller.C1,controller.N1,controller.S1); }
2. 复用已有的控制器实例(关键优化)
不要每次点击都重新加载fxml,而是获取已经显示在界面上的table_scene控制器实例。比如:
- 在打开
table_scene场景时,保存控制器引用:
// 假设你在某个地方打开table_scene场景 FXMLLoader loader = new FXMLLoader(getClass().getResource("table_scene.fxml")); Parent root = loader.load(); // 把控制器保存为类的成员变量,后续直接复用 table_scene tableController = loader.getController(); Stage stage = new Stage(); stage.setScene(new Scene(root)); stage.show();
然后在setTable方法中,直接使用保存好的控制器实例,无需重新加载fxml:
public void setTable(ActionEvent event)throws IOException{ // 直接复用已有的控制器实例 tableController.C1.setCellValueFactory(new PropertyValueFactory<>("Classs")); tableController.N1.setCellValueFactory(new PropertyValueFactory<>("Name")); tableController.S1.setCellValueFactory(new PropertyValueFactory<>("Sec")); // 先清空列避免重复,再添加并设置数据 tableController.table1.getColumns().clear(); tableController.table1.getColumns().addAll(tableController.C1,tableController.N1,tableController.S1); tableController.table1.setItems(getDetails()); }
3. 修复abc2类中的笔误
看abc2类的代码,你写了controller.S2.setCellValueFactory,但table_scene控制器中对应的列是V2,这里是明显的笔误,要改成:
controller.V2.setCellValueFactory(new PropertyValueFactory<>("Sec"));
否则会触发空指针异常。
额外优化建议
把列的初始化逻辑放到table_scene控制器的initialize方法中,更符合JavaFX的MVC规范:
public class table_scene implements Initializable { @FXML TableView<TableItems> table1; @FXML TableView<TableItems> table2; @FXML TableColumn<TableItems,String> N1; @FXML TableColumn<TableItems,Integer> C1; @FXML TableColumn<TableItems,String> S1; @FXML TableColumn<TableItems,String> N2; @FXML TableColumn<TableItems,Integer> C2; @FXML TableColumn<TableItems,String> V2; @Override public void initialize(URL url, ResourceBundle resourceBundle) { // 初始化table1的列映射 C1.setCellValueFactory(new PropertyValueFactory<>("Classs")); N1.setCellValueFactory(new PropertyValueFactory<>("Name")); S1.setCellValueFactory(new PropertyValueFactory<>("Sec")); table1.getColumns().addAll(C1,N1,S1); // 初始化table2的列映射 C2.setCellValueFactory(new PropertyValueFactory<>("Classs")); N2.setCellValueFactory(new PropertyValueFactory<>("Name")); V2.setCellValueFactory(new PropertyValueFactory<>("Sec")); table2.getColumns().addAll(C2,N2,V2); } }
这样外部类只需要负责设置表格数据即可:
public void setTable(ActionEvent event)throws IOException{ tableController.table1.setItems(getDetails()); }
内容的提问来源于stack exchange,提问作者D buiss

