JTable开启自动排序后添加行触发数组越界异常问题
解决JTable排序后添加行触发ArrayIndexOutOfBoundsException的问题
这个问题的核心原因很明确:当你开启setAutoCreateRowSorter(true)后,JTable的视图索引和TableModel的模型索引不再一一对应。你的TableModelListener中拿到的arg0.getFirstRow()是模型层面的行索引,但直接调用table.getValueAt(row, 0)时,JTable会把这个索引当成视图层面的索引来使用——排序后两者的映射关系已经改变,自然会出现索引越界的错误。
两种可行的解决方案
方案1:直接从TableModel获取数据(最简单)
既然事件是来自TableModel的,完全不需要通过JTable来获取新增行的数据,直接从tableModel里取就好,模型索引在模型里肯定是有效的:
tableModel.addTableModelListener(new TableModelListener(){ @Override public void tableChanged(TableModelEvent arg0) { if(arg0.getType() == TableModelEvent.INSERT){ int modelRow = arg0.getFirstRow(); // 直接从模型获取值,不需要经过JTable System.out.println(tableModel.getValueAt(modelRow, 0)); } } });
方案2:将模型索引转换为视图索引(如果必须通过JTable操作)
如果你确实需要通过JTable来操作这行数据(比如选中它、修改UI样式等),需要用JTable.convertRowIndexToView()方法把模型索引转换成视图索引,同时记得判断转换后的索引是否有效(避免极端情况):
tableModel.addTableModelListener(new TableModelListener(){ @Override public void tableChanged(TableModelEvent arg0) { if(arg0.getType() == TableModelEvent.INSERT){ int modelRow = arg0.getFirstRow(); // 转换模型索引到视图索引 int viewRow = table.convertRowIndexToView(modelRow); // 检查转换后的索引是否有效(排序后新增行肯定在视图里,这步是防御性编程) if(viewRow != -1){ System.out.println(table.getValueAt(viewRow, 0)); } } } });
为什么你之前的尝试没生效?
你提到尝试过转换行索引但没解决问题,大概率是搞反了转换方向:比如误用了convertViewIndexToModel()(把视图索引转模型索引),而不是convertRowIndexToView()(把模型索引转视图索引)。另外,直接用insertRow()和addRow()本质上没有区别,核心问题还是索引的混淆。
修改后的完整SSCCE示例
这里用方案1的写法修改你的代码,确保可以正常运行:
import java.awt.BorderLayout; import java.awt.FlowLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.DefaultTableModel; import java.awt.GridLayout; import javax.swing.JTable; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import javax.swing.JScrollPane; public class MainTable extends JDialog { private static final long serialVersionUID = 156332386872772726L; private final JPanel contentPanel = new JPanel(); private DefaultTableModel tableModel; private JTable table; public static void main(String[] args) { try { MainTable dialog = new MainTable(); dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); dialog.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } public MainTable() { setBounds(100, 100, 450, 300); getContentPane().setLayout(new BorderLayout()); contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); getContentPane().add(contentPanel, BorderLayout.CENTER); contentPanel.setLayout(new GridLayout(1, 0, 0, 0)); { JScrollPane scrollPane = new JScrollPane(); contentPanel.add(scrollPane); { table = new JTable(); table.setAutoCreateRowSorter(true); tableModel = new DefaultTableModel(new String[]{"first","second"},0); table.setModel(tableModel); tableModel.addTableModelListener(new TableModelListener(){ @Override public void tableChanged(TableModelEvent arg0) { if(arg0.getType() == TableModelEvent.INSERT){ int modelRow = arg0.getFirstRow(); // 直接从模型获取数据,避免索引混淆 System.out.println(tableModel.getValueAt(modelRow, 0)); } } }); scrollPane.setViewportView(table); } } { JPanel buttonPane = new JPanel(); buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); getContentPane().add(buttonPane, BorderLayout.SOUTH); { JButton okButton = new JButton("Add"); okButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { int row = tableModel.getRowCount(); tableModel.addRow(new Object[]{row,row}); } }); buttonPane.add(okButton); getRootPane().setDefaultButton(okButton); } } } }
内容的提问来源于stack exchange,提问作者lution
相关产品推荐
相关产品推荐

