如何修复 Java 中的 'TableModel.setValueAt ArrayIndexOutOfBoundsException' 错误

How to fix 'TableModel.setValueAt ArrayIndexOutOfBoundsException' error in Java

我有一个 JTable 应用程序。我需要更改单元格值并保存数据,但只有索引小于或等于 4 的单元格在数组范围内。

package fi.allu.neliojuuri;

import com.sun.glass.events.KeyEvent;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import javax.swing.event.CellEditorListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

public class Neliojuuri extends JPanel
    implements ActionListener, ItemListener, TableModelListener {

String newline = "\n";
private JTable table;
private JCheckBox rowCheck;
private JCheckBox columnCheck;
private JCheckBox cellCheck;
private ButtonGroup buttonGroup;
private JTextArea output;

private String[] sarakenimet = new String[70];
private String[] sisakkainen = new String[70];
private Object[][] ulkoinen = new String[71][71];

private DefaultTableModel oletusmalli = null;
private CellEditorListener solumuokkaaja = null;

public Neliojuuri() {
    super();
    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));        

    for (int i = 0; i < 70; i++) {
            sarakenimet[i] = String.valueOf(i + 1);
        }

        for (int i = 0; i < sisakkainen.length; i++) {
            sisakkainen[i] = String.valueOf(i + 1);
        }

        for (int i = 0; i < ulkoinen.length; i++) {
            ulkoinen[i] = sisakkainen;
        }

    table = new JTable(ulkoinen, sarakenimet);
    table.setPreferredScrollableViewportSize(new Dimension(500, 210));
    table.setFillsViewportHeight(true);
    table.setRowHeight(30);

    TableColumnModel sarakeMalli = table.getColumnModel();
    for (int i = 0; i < 70; i++) {
        sarakeMalli.getColumn(i).setPreferredWidth(75);
    }

    oletusmalli = new javax.swing.table.DefaultTableModel();
    table.setModel(oletusmalli);

    String[] sarakenimet = new String[70];
    for (int i = 0; i < 70; i++) {
        sarakenimet[i] = String.valueOf(i + 1);
    }

    String[] sisakkainen = new String[70];
    for (int i = 0; i < sisakkainen.length; i++) {
        sisakkainen[i] = String.valueOf(i + 1);
    }
    Object[][] ulkoinen = new String[71][71];
    for (int i = 0; i < ulkoinen.length; i++) {
        ulkoinen[i] = sisakkainen;
    }

    oletusmalli.setColumnIdentifiers(sarakenimet);

    for (int count = 0; count < 70; count++){
        oletusmalli.insertRow(count, sisakkainen);
    }

    table.getSelectionModel().addListSelectionListener(new RowListener());
    table.getColumnModel().getSelectionModel().
            addListSelectionListener(new ColumnListener());
    add(new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 
        JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

    table.getModel().addTableModelListener(this);

    add(new JLabel("Selection Mode"));
    buttonGroup = new ButtonGroup();
    addRadio("Multiple Interval Selection").setSelected(true);
    addRadio("Single Selection");
    addRadio("Single Interval Selection");

    add(new JLabel("Selection Options"));
    rowCheck = addCheckBox("Row Selection");
    rowCheck.setSelected(true);
    columnCheck = addCheckBox("Column Selection");
    cellCheck = addCheckBox("Cell Selection");
    cellCheck.setEnabled(false);

    output = new JTextArea(5, 40);
    output.setEditable(false);
    output.setFont(new Font("Segoe UI", Font.PLAIN, 20));
    add(new JScrollPane(output));
}

private JCheckBox addCheckBox(String text) {
    JCheckBox checkBox = new JCheckBox(text);
    checkBox.addActionListener(this);
    add(checkBox);
    return checkBox;
}

private JRadioButton addRadio(String text) {
    JRadioButton b = new JRadioButton(text);
    b.addActionListener(this);
    buttonGroup.add(b);
    add(b);
    return b;
}

public void actionPerformed(ActionEvent e) {        
    JFileChooser tiedostovalitsin = new JFileChooser();        

    JMenuItem source = (JMenuItem)(e.getSource());
    if (source.getText() == "Save") {
        int palautusArvo = tiedostovalitsin.showSaveDialog(Neliojuuri.this);

        if (palautusArvo == JFileChooser.APPROVE_OPTION) {
            File tiedosto = tiedostovalitsin.getSelectedFile();
            // Tallenna tiedosto oikeasti
            for (int i = 0; i < oletusmalli.getRowCount(); i++) {
                System.out.println(oletusmalli.getValueAt(1, i).toString());
            }
            System.out.println(tiedosto.getName());
        }
    }

    String command = e.getActionCommand();
    //Cell selection is disabled in Multiple Interval Selection
    //mode. The enabled state of cellCheck is a convenient flag
    //for this status.
    if ("Row Selection" == command) {
        table.setRowSelectionAllowed(rowCheck.isSelected());
        //In MIS mode, column selection allowed must be the
        //opposite of row selection allowed.
        if (!cellCheck.isEnabled()) {
            table.setColumnSelectionAllowed(!rowCheck.isSelected());
        }
    } else if ("Column Selection" == command) {
        table.setColumnSelectionAllowed(columnCheck.isSelected());
        //In MIS mode, row selection allowed must be the
        //opposite of column selection allowed.
        if (!cellCheck.isEnabled()) {
            table.setRowSelectionAllowed(!columnCheck.isSelected());
        }
    } else if ("Cell Selection" == command) {
        table.setCellSelectionEnabled(cellCheck.isSelected());
    } else if ("Multiple Interval Selection" == command) {
        table.setSelectionMode(
                ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        //If cell selection is on, turn it off.
        if (cellCheck.isSelected()) {
            cellCheck.setSelected(false);
            table.setCellSelectionEnabled(false);
        }
        //And don't let it be turned back on.
        cellCheck.setEnabled(false);
    } else if ("Single Interval Selection" == command) {
        table.setSelectionMode(
                ListSelectionModel.SINGLE_INTERVAL_SELECTION);
        //Cell selection is ok in this mode.
        cellCheck.setEnabled(true);
    } else if ("Single Selection" == command) {
        table.setSelectionMode(
                ListSelectionModel.SINGLE_SELECTION);
        //Cell selection is ok in this mode.
        cellCheck.setEnabled(true);
    }

    //Update checkboxes to reflect selection mode side effects.
    rowCheck.setSelected(table.getRowSelectionAllowed());
    columnCheck.setSelected(table.getColumnSelectionAllowed());
    if (cellCheck.isEnabled()) {
        cellCheck.setSelected(table.getCellSelectionEnabled());
    }
}

private void outputSelection() {
    output.append(String.format("Lead: %d, %d. ",
            table.getSelectionModel().getLeadSelectionIndex(),
            table.getColumnModel().getSelectionModel().
                    getLeadSelectionIndex()));
    output.append("Rows:");
    for (int c : table.getSelectedRows()) {
        output.append(String.format(" %d", c));
    }
    output.append(". Columns:");
    for (int c : table.getSelectedColumns()) {
        output.append(String.format(" %d", c));
    }
    output.append(".\n");
}

@Override
public void itemStateChanged(ItemEvent e) {
    throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}

@Override
public void tableChanged(TableModelEvent e) {
    int rivi = e.getFirstRow();
    int sarake = e.getColumn();
    TableModel malli = (TableModel)e.getSource();
    String sarakeNimi = malli.getColumnName(sarake);
    Object data = malli.getValueAt(rivi, sarake);
    MyTableModel taulumalli = new MyTableModel();
    taulumalli.setValueAt(data, rivi, sarake);
    System.out.println("rivi: " + rivi + " sarake: " + sarake + " data: " + data);
}

private void setValueAt(Object data, int rivi, int sarake) {
    throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}

private class RowListener implements ListSelectionListener {

    public void valueChanged(ListSelectionEvent event) {
        if (event.getValueIsAdjusting()) {
            return;
        }
        output.append("ROW SELECTION EVENT. ");
        outputSelection();
    }
}

private class ColumnListener implements ListSelectionListener {

    public void valueChanged(ListSelectionEvent event) {
        if (event.getValueIsAdjusting()) {
            return;
        }
        output.append("COLUMN SELECTION EVENT. ");
        outputSelection();
    }
}

class MyTableModel extends AbstractTableModel {

    private String[] columnNames = {"First Name",
        "Last Name",
        "Sport",
        "# of Years",
        "Vegetarian"};
    private Object[][] data = {
        {"Kathy", "Smith",
            "Snowboarding", new Integer(5), new Boolean(false)},
        {"John", "Doe",
            "Rowing", new Integer(3), new Boolean(true)},
        {"Sue", "Black",
            "Knitting", new Integer(2), new Boolean(false)},
        {"Jane", "White",
            "Speed reading", new Integer(20), new Boolean(true)},
        {"Joe", "Brown",
            "Pool", new Integer(10), new Boolean(false)}
    };

    public int getColumnCount() {
        return columnNames.length;
    }

    public int getRowCount() {
        return data.length;
    }

    public String getColumnName(int col) {
        return columnNames[col];
    }

    public Object getValueAt(int row, int col) {
        return data[row][col];
    }

    /*
     * JTable uses this method to determine the default renderer/
     * editor for each cell.  If we didn't implement this method,
     * then the last column would contain text ("true"/"false"),
     * rather than a check box.
     */
    public Class getColumnClass(int c) {
        return getValueAt(0, c).getClass();
    }

    /*
     * Don't need to implement this method unless your table's
     * editable.
     */
    public boolean isCellEditable(int row, int col) {
        //Note that the data/cell address is constant,
        //no matter where the cell appears onscreen.
        if (col < 2) {
            return false;
        } else {
            return true;
        }
    }

    /*
     * Don't need to implement this method unless your table's
     * data can change.
     */
    public void setValueAt(Object value, int row, int col) {
        data[row][col] = value;
        fireTableCellUpdated(row, col);
    }

}

public JMenuBar luoValikkoPalkki() {
    JMenuBar valikkopalkki = new JMenuBar();
    JMenu valikko = new JMenu("File");
    valikko.setMnemonic(KeyEvent.VK_F);
    valikko.getAccessibleContext().setAccessibleDescription(
            "File saving menu");
    valikkopalkki.add(valikko);

    JMenuItem valikkoitem = new JMenuItem("Save", KeyEvent.VK_S);
    valikkoitem.setAccelerator(KeyStroke.getKeyStroke(
            java.awt.event.KeyEvent.VK_S, ActionEvent.CTRL_MASK));
    valikkoitem.addActionListener(this);
    valikko.add(valikkoitem);
    return valikkopalkki;
}

/**
 * Create the GUI and show it. For thread safety, this method should be
 * invoked from the event-dispatching thread.
 */
private static void createAndShowGUI() {
    //Disable boldface controls.
    UIManager.put("swing.boldMetal", Boolean.FALSE);

    try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (Exception e) {
        e.printStackTrace();
    }
    //Create and set up the window.
    JFrame frame = new JFrame("Neliöjuuri");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    Neliojuuri neliojuuri = new Neliojuuri();
    frame.setJMenuBar(neliojuuri.luoValikkoPalkki());

    //Create and set up the content pane.
    Neliojuuri newContentPane = new Neliojuuri();
    newContentPane.setOpaque(true); //content panes must be opaque
    frame.setContentPane(newContentPane);

    //Display the window.
    frame.pack();
    frame.setVisible(true);
}

public static void main(String[] args) {
    //Schedule a job for the event-dispatching thread:
    //creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            createAndShowGUI();
        }
    });
}
}

堆栈跟踪:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 5
at fi.allu.neliojuuri.Neliojuuri$MyTableModel.setValueAt(Neliojuuri.java:356)
at fi.allu.neliojuuri.Neliojuuri.tableChanged(Neliojuuri.java:261)
at javax.swing.table.AbstractTableModel.fireTableChanged(AbstractTableModel.java:296)
at javax.swing.table.AbstractTableModel.fireTableCellUpdated(AbstractTableModel.java:275)
at javax.swing.table.DefaultTableModel.setValueAt(DefaultTableModel.java:666)
at javax.swing.JTable.setValueAt(JTable.java:2744)
at javax.swing.JTable.editingStopped(JTable.java:4729)
at javax.swing.AbstractCellEditor.fireEditingStopped(AbstractCellEditor.java:141)
at javax.swing.DefaultCellEditor$EditorDelegate.stopCellEditing(DefaultCellEditor.java:368)
at javax.swing.DefaultCellEditor.stopCellEditing(DefaultCellEditor.java:233)
at javax.swing.JTable$GenericEditor.stopCellEditing(JTable.java:5473)
at javax.swing.DefaultCellEditor$EditorDelegate.actionPerformed(DefaultCellEditor.java:385)
at javax.swing.JTextField.fireActionPerformed(JTextField.java:508)
at javax.swing.JTextField.postActionEvent(JTextField.java:721)
at javax.swing.JTextField$NotifyAction.actionPerformed(JTextField.java:836)
at javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1668)
at javax.swing.JComponent.processKeyBinding(JComponent.java:2882)
at javax.swing.JComponent.processKeyBindings(JComponent.java:2929)
at javax.swing.JComponent.processKeyEvent(JComponent.java:2845)
at java.awt.Component.processEvent(Component.java:6316)
at java.awt.Container.processEvent(Container.java:2239)
at java.awt.Component.dispatchEventImpl(Component.java:4889)
at java.awt.Container.dispatchEventImpl(Container.java:2297)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1954)
at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:835)
at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1103)
at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:974)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:800)
at java.awt.Component.dispatchEventImpl(Component.java:4760)
at java.awt.Container.dispatchEventImpl(Container.java:2297)
at java.awt.Window.dispatchEventImpl(Window.java:2746)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:760)
at java.awt.EventQueue.access0(EventQueue.java:97)
at java.awt.EventQueue.run(EventQueue.java:709)
at java.awt.EventQueue.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:84)
at java.awt.EventQueue.run(EventQueue.java:733)
at java.awt.EventQueue.run(EventQueue.java:731)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:730)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82) 

我希望能够更改 table 中的所有单元格值,但我只能更改其中的一部分。索引大于或等于 5 的单元格 return ArrayIndexOutOfBounds 错误。

Cells with index greater than or equal to 5 return ArrayIndexOutOfBounds error.

因为你创建你的"MyTableModel"里面只有5行数据。如果模型中存在 row/column,您只能更改数据。

您为什么要使用 Swing 教程中的 "MyTableModel" 作为您的 TableModel?

此外,为什么每次生成 "tableChanged" 事件时都要创建一个新的 "MyTableModel"?这意味着您将丢失之前的更改。

我建议,如果您尝试将数据从一个 table 复制到另一个,那么您需要创建一个 DefaultTableModel,其中 row/column 的数量与现有的相同在生成 "tableChanged" 事件的其他 TableModel 中。

但在不了解您的真实需求的情况下,我们无法给出合适的解决方案,只能排除错误。

当执行到 tableChanged() 内的第 taulumalli.setValueAt(data, rivi, sarake); 行时,有 2 个 table 模型。

第一个 table 模型由行 table = new JTable(ulkoinen, sarakenimet); 创建。这个模型实际上连接到显示的table。它有 70 行和 70 列。

第二个 table 模型是在 tableChanged() 中通过行 MyTableModel taulumalli = new MyTableModel(); 创建的。尽管这是一个 table 模型,但它没有连接到任何 table。这有 5 行和 5 列。

当用户编辑 table 中的值时,将调用 tableChanged()。在这个方法中,在前两行中,你得到的行索引和列索引来自第一个 table 模型(因为显示的 table 连接到这个 table 模型)。然后将第一个 table 模型的这些行索引和列索引传递给第二个 table 模型的 setValueAt()。这是不正确的,因为 2 table 模型具有不同的结构。这会导致错误。