使用LinkedList读写文件到JFrame的JTable时遇IndexOutOfBoundsException
问题分析与修复方案
核心错误点
1. addContactToFile 方法逻辑错误
原代码直接通过 fileContacts.get(0)、get(1)、get(2) 读取数据,完全误解了二维LinkedList的结构:fileContacts 中每个元素是单个联系人的信息集合(包含姓名、电话、邮箱),而非所有联系人的单个字段。第一次添加联系人时,fileContacts 只有1个元素,调用 get(1) 直接触发 IndexOutOfBoundsException。同时该方法接收整个联系人集合作为参数完全多余,只需传入刚创建的单个联系人即可。
2. loadFileContents 方法未清空全局集合
每次加载文件时,fileContacts 没有被清空,导致旧数据与新读取的数据累加,不仅造成表格重复显示,还会在后续操作中引发索引混乱。
3. 文件读写格式不匹配
原代码写入时额外添加空行,但读取时未跳过空行,会导致空行被当作联系人字段存入集合,破坏“每3行一个联系人”的结构。
修复后的完整代码
import java.awt.*; import java.awt.event.*; import java.io.*; import javax.swing.*; import javax.swing.table.*; import java.util.LinkedList; public class FileContentApp { private static final String FILE_PATH = "contacts.txt"; private static DefaultTableModel tableModel; public static LinkedList<LinkedList<String>> fileContacts = new LinkedList<>(); public static void main(String[] args) { JFrame frame = new JFrame("Contact File"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 300); frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().setBackground(Color.WHITE); JPanel topPanel = new JPanel(new BorderLayout()); topPanel.setBackground(Color.WHITE); JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); buttonPanel.setBackground(Color.WHITE); JLabel titleLabel = new JLabel("Contacts"); titleLabel.setHorizontalAlignment(SwingConstants.CENTER); topPanel.add(titleLabel, BorderLayout.NORTH); JButton addButton = new JButton("+"); addButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showDialog(frame); } }); JButton settingButton = new JButton("..."); buttonPanel.add(addButton); buttonPanel.add(settingButton); topPanel.add(buttonPanel, BorderLayout.EAST); frame.getContentPane().add(topPanel, BorderLayout.NORTH); JPanel contentPanel = new JPanel(new BorderLayout()); contentPanel.setBackground(Color.WHITE); JTable table = createTable(); JScrollPane scrollPane = new JScrollPane(table); contentPanel.add(scrollPane, BorderLayout.CENTER); frame.getContentPane().add(contentPanel, BorderLayout.CENTER); loadFileContents(); frame.setLocationRelativeTo(null); frame.setVisible(true); } private static JTable createTable() { tableModel = new DefaultTableModel(); tableModel.addColumn("Name"); tableModel.addColumn("Phone"); tableModel.addColumn("Email"); JTable table = new JTable(tableModel); table.setGridColor(Color.LIGHT_GRAY); table.setShowGrid(true); return table; } private static void showDialog(JFrame parentFrame) { LinkedList<String> listContact = new LinkedList<>(); JDialog dialog = new JDialog(parentFrame, "Add Contact", true); dialog.setSize(300, 200); dialog.setLayout(new BorderLayout()); dialog.setLocationRelativeTo(parentFrame); JPanel inputPanel = new JPanel(new GridLayout(3, 2)); inputPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); JLabel nameLabel = new JLabel("Name : "); JTextField nameField = new JTextField(); JLabel phoneLabel = new JLabel("Phone : "); JTextField phoneField = new JTextField(); JLabel emailLabel = new JLabel("Email : "); JTextField emailField = new JTextField(); inputPanel.add(nameLabel); inputPanel.add(nameField); inputPanel.add(phoneLabel); inputPanel.add(phoneField); inputPanel.add(emailLabel); inputPanel.add(emailField); JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); JButton addButton = new JButton("Add"); addButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String name = nameField.getText(); String phone = phoneField.getText(); String email = emailField.getText(); listContact.add(name); listContact.add(phone); listContact.add(email); fileContacts.add(listContact); addContactToFile(listContact); // 传入单个联系人而非整个集合 loadFileContents(); dialog.dispose(); } }); buttonPanel.add(addButton); dialog.add(inputPanel, BorderLayout.CENTER); dialog.add(buttonPanel, BorderLayout.SOUTH); dialog.setVisible(true); } private static void addContactToFile(LinkedList<String> contact) { try (BufferedWriter writer = new BufferedWriter(new FileWriter(FILE_PATH, true))) { // 写入单个联系人的三个字段,每行一个 writer.write(contact.get(0)); writer.newLine(); writer.write(contact.get(1)); writer.newLine(); writer.write(contact.get(2)); writer.newLine(); } catch (IOException e) { e.printStackTrace(); } } private static void loadFileContents() { tableModel.setRowCount(0); // 清空表格 fileContacts.clear(); // 清空全局联系人集合,避免数据累加 try (BufferedReader reader = new BufferedReader(new FileReader(FILE_PATH))) { String line; int lineCount = 0; LinkedList<String> currentContact = null; while ((line = reader.readLine()) != null) { line = line.trim(); if (line.isEmpty()) { continue; // 跳过空行,避免破坏结构 } if (lineCount % 3 == 0) { currentContact = new LinkedList<>(); fileContacts.add(currentContact); } currentContact.add(line); lineCount++; } // 将集合数据同步到表格 for (LinkedList<String> contact : fileContacts) { // 确保每个联系人有三个字段,避免表格报错 if (contact.size() == 3) { tableModel.addRow(contact.toArray()); } } } catch (IOException e) { e.printStackTrace(); } } }
额外优化建议
- 封装联系人数据:创建一个
Contact类(包含name、phone、email属性)替代二维LinkedList,代码可读性和可维护性会大幅提升,也能避免集合操作的索引错误。 - 输入验证:在添加联系人时,检查姓名、电话等字段是否为空,避免无效数据写入文件。
- 删除功能扩展:可通过
JTable的选中事件获取行索引,调用fileContacts.remove(index)和tableModel.removeRow(index)实现逐条删除,同时同步更新文件。
内容的提问来源于stack exchange,提问作者Franco Luis Rosel
相关产品推荐
相关产品推荐

