You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

复制JTable指定列到新JTable时触发数组越界异常问题排查

JTable复制指定列时ArrayIndexOutOfBoundsException问题排查与解决

问题背景

在Frame中展示完整JTable,需在对话框中展示该表的指定列精简版本。通过创建新TableModel和TableColumnModel复制原表、跳过不需要的列时,对话框显示触发无限错误循环,起始异常为ArrayIndexOutOfBoundsException: 3 >= 2。精简表仅有2列,但列索引3超出范围,调用getColumnCount()检查列数结果正常。尝试用DefaultTableModel.addColumn()方法可成功复制,但丢失列宽信息,需保留列宽的前提下解决当前错误。

原代码

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

public class CopyTable extends JFrame {
  public static final long serialVersionUID = 100L;

  public CopyTable() {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(null);

    JTable table= createTable();
    JScrollPane scroll= new JScrollPane(table);
    add(scroll, BorderLayout.CENTER);
    JButton b= new JButton("Show dialog");
    b.addActionListener(e -> createAndShowDialog(table));
    add(b, BorderLayout.SOUTH);
    setSize(table.getColumnModel().getTotalColumnWidth()+11, 240);
    setVisible(true);
  }


  static public void main(String args[]) {
    EventQueue.invokeLater(CopyTable::new);
  }


// @param columns   Indices of columns to be copied into the new table.
  public JTable copyTable(JTable table, int... columns) {
    DefaultTableModel tblModel= new DefaultTableModel(0, columns.length);
    DefaultTableModel oldModel= (DefaultTableModel)table.getModel();
    Object[] row= new Object[columns.length];
    int iRow= 0;
    while (iRow<oldModel.getRowCount()) {
      for (int i=0; i<columns.length; i++) {
        row[i]= oldModel.getValueAt(iRow, columns[i]);
      }
      tblModel.addRow(row);
      iRow++;
    }
    DefaultTableColumnModel colModel= new DefaultTableColumnModel();
    DefaultTableColumnModel oldColModel=
                (DefaultTableColumnModel)table.getColumnModel();
    for (int i=0; i<columns.length; i++) {
      colModel.addColumn(oldColModel.getColumn(columns[i]));
/*      Creating a new column didn't help.
      TableColumn tc= new TableColumn();
      tc= oldColModel.getColumn(columns[i]);
      colModel.addColumn(tc);
*/
    }
    return new JTable(tblModel, colModel);
  }


  private void createAndShowDialog(JTable table) {
    JTable tbl = copyTable(table, 0, 3); // Copy only columns 0 and 3.
    JOptionPane.showMessageDialog(this, ""+tbl.getModel().getRowCount()+", "+ 
            tbl.getModel().getColumnCount()+", "+ 
            tbl.getColumnModel().getColumnCount(),
            "Checking row/column count",
            JOptionPane.INFORMATION_MESSAGE);
    JDialog dlg= new JDialog(this, "Reduced table", true);
    dlg.setLocationRelativeTo(this);
    JScrollPane scroll = new JScrollPane(tbl);
    dlg.add(scroll, BorderLayout.CENTER);
    dlg.pack();
    dlg.setVisible(true);
  }


  private JTable createTable() {
    String headers[] = {"Fruit", "Colour", "Count", "Price"};
    Object data[][] = {
        {"Apple", "Green", 6, .3},
        {"Banana", "Yellow", 3, .4},
        {"Cherry", "Red", 10, 1.1}
      };

    DefaultTableModel model = new DefaultTableModel(data, headers) {
      public Class<?> getColumnClass(int column) {
        Class<?> returnValue;
        if (column>=0 && column<getColumnCount() && getValueAt(0,column)!=null)
          returnValue= getValueAt(0, column).getClass();
        else
          returnValue= Object.class;
        return returnValue;
      }
    };
    JTable tbl= new JTable(model);
    TableColumnModel tcm= tbl.getColumnModel();
    int[] width= new int[] {60, 50, 40, 40};
    for (int i=0; i<headers.length; i++) {
      tcm.getColumn(i).setPreferredWidth(width[i]);
      tcm.getColumn(i).setWidth(width[i]);
    }
    return tbl;
  }

}

异常信息

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 3 >= 2
        at java.base/java.util.Vector.elementAt(Vector.java:466)
        at java.desktop/javax.swing.table.DefaultTableModel.getValueAt(DefaultTableModel.java:661)
        at java.desktop/javax.swing.JTable.getValueAt(JTable.java:2763)
        at java.desktop/javax.swing.JTable.prepareRenderer(JTable.java:5780)
        at java.desktop/javax.swing.plaf.basic.BasicTableUI.paintCell(BasicTableUI.java:2207)
        at java.desktop/javax.swing.plaf.basic.BasicTableUI.paintCells(BasicTableUI.java:2109)
        at java.desktop/javax.swing.plaf.basic.BasicTableUI.paint(BasicTableUI.java:1905)
        at java.desktop/javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
        at java.desktop/javax.swing.JComponent.paintComponent(JComponent.java:852)

问题原因

直接从原TableColumnModel获取的TableColumn对象保留了原表的modelIndex。例如原表第3列的modelIndex是3,但新创建的TableModel只有2列(索引0和1)。当JTable渲染单元格时,TableColumn会用自身的modelIndex去新TableModel中取值,导致索引越界。

解决方案

复制TableColumn时,需要创建原列的副本,并将副本的modelIndex设置为新表中的列索引(即循环变量i),确保与新TableModel的列索引对应。

修改后的copyTable方法:

public JTable copyTable(JTable table, int... columns) {
    DefaultTableModel tblModel= new DefaultTableModel(0, columns.length);
    DefaultTableModel oldModel= (DefaultTableModel)table.getModel();
    Object[] row= new Object[columns.length];
    int iRow= 0;
    while (iRow<oldModel.getRowCount()) {
        for (int i=0; i<columns.length; i++) {
            row[i]= oldModel.getValueAt(iRow, columns[i]);
        }
        tblModel.addRow(row);
        iRow++;
    }
    DefaultTableColumnModel colModel= new DefaultTableColumnModel();
    DefaultTableColumnModel oldColModel= (DefaultTableColumnModel)table.getColumnModel();
    for (int i=0; i<columns.length; i++) {
        // 创建原TableColumn的副本,避免引用原对象的modelIndex
        TableColumn originalCol = oldColModel.getColumn(columns[i]);
        TableColumn newCol = new TableColumn();
        // 复制原列的属性:表头、宽度、渲染器等
        newCol.setHeaderValue(originalCol.getHeaderValue());
        newCol.setPreferredWidth(originalCol.getPreferredWidth());
        newCol.setWidth(originalCol.getWidth());
        newCol.setMinWidth(originalCol.getMinWidth());
        newCol.setMaxWidth(originalCol.getMaxWidth());
        newCol.setCellRenderer(originalCol.getCellRenderer());
        newCol.setCellEditor(originalCol.getCellEditor());
        // 设置新的modelIndex,对应新TableModel的列索引
        newCol.setModelIndex(i);
        colModel.addColumn(newCol);
    }
    return new JTable(tblModel, colModel);
}

关键修改点

  1. 不直接添加原TableColumn对象,而是创建副本,避免继承原列的modelIndex。
  2. 复制原列的所有关键属性(表头、宽度、渲染器等),保留原列的显示样式。
  3. 将新列的modelIndex设置为当前循环的索引i,确保与新TableModel的列索引匹配。

内容的提问来源于stack exchange,提问作者Jörg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 17:03:20