如何在保留JTable原始数据的前提下计算指定列值并汇总到另一JTable?
问题描述
我已向JTable(dataTable)中添加了数据,需要通过按钮点击事件将最后一列的指定值求和后展示到另一个JTable(summeryTable)中。要求保留dataTable的原始数据,但当前求和过程中原始数据会被修改,请问实现该需求的最佳方式是什么?
现有代码
MyApplication类
public class MyApplication { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } private static void createAndShowGUI() { JFrame frame = new JFrame("My Application"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new DataPanel()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }
DataPanel类
public class DataPanel extends JPanel { private JButton sumButton = new JButton(); private JTable dataTable = new JTable(); private JTable summeryTable = new JTable(); public DataPanel() { GridBagConstraints gbc = new GridBagConstraints(); setLayout(new GridBagLayout()); sumButton.setText("Get Sum"); dataTable.setModel(new javax.swing.table.DefaultTableModel( new Object[][]{ {"Column", null, null, null}, {null,"01","T20","500"}, {null, "01", "R6", "250"}, {null, "02", "R6", "50"}, {"Slab", null, null, null}, {null, "03", "T10", "300"}, {null, "04", "T10", "150"} }, new String[]{ "Member", "Bar mark", "Type & Size", "Weight" } )); summeryTable.setModel(new javax.swing.table.DefaultTableModel( new Object[][]{}, new String[]{ "Type & Size", " Total Weight" } )); JScrollPane dTableContainer = new JScrollPane(dataTable); JScrollPane sTableContainer = new JScrollPane(summeryTable); gbc.insets = new Insets(5, 5, 5, 5); gbc.fill = GridBagConstraints.BOTH; gbc.weightx = 0.1; gbc.weighty = 0.1; add(dTableContainer, gbc); gbc.insets = new Insets(5, 5, 5, 5); gbc.fill = GridBagConstraints.BOTH; gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 0.1; gbc.weighty = 0.1; add(sTableContainer, gbc); gbc.insets = new Insets(5, 5, 5, 5); gbc.fill = GridBagConstraints.NONE; gbc.gridx = 0; gbc.gridy = 2; gbc.weightx = 0.1; gbc.weighty = 0.1; add(sumButton, gbc); sumButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { DefaultTableModel dataModel = (DefaultTableModel) dataTable.getModel(); DefaultTableModel summeryModel = (DefaultTableModel) summeryTable.getModel(); BigDecimal tWeightRsix = BigDecimal.ZERO; int rowCount = summeryModel.getRowCount(); for (int j = rowCount - 1; j >= 0; j--) { summeryModel.removeRow(j); } for (int i = 0; i < dataModel.getRowCount(); i++) { if (dataModel.getValueAt(i, 2) == null) { dataModel.removeRow(i); } String mark = (String) dataModel.getValueAt(i, 2); if (mark.equals("R6")) { BigDecimal weights = new BigDecimal((String) dataModel.getValueAt(i, 3).toString()); tWeightRsix = tWeightRsix.add(weights).setScale(2, RoundingMode.HALF_UP); } } Object data1 = "R6"; Object data2 = tWeightRsix; summeryModel.addRow(new Object[]{data1, data2}); } }); } }
解决方案
当前代码的问题在于求和循环中直接调用dataModel.removeRow(i)删除了原始TableModel中的行,导致dataTable的原始数据被修改。要保留原始数据,核心原则是只读遍历原始数据,不修改原始TableModel,具体实现方式如下:
1. 快速修复:修改求和逻辑
在遍历原始数据时,跳过那些Type & Size列为null的行,而不是删除它们。修改按钮点击事件的逻辑:
sumButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { DefaultTableModel dataModel = (DefaultTableModel) dataTable.getModel(); DefaultTableModel summeryModel = (DefaultTableModel) summeryTable.getModel(); BigDecimal tWeightRsix = BigDecimal.ZERO; // 清空汇总表 int rowCount = summeryModel.getRowCount(); for (int j = rowCount - 1; j >= 0; j--) { summeryModel.removeRow(j); } // 遍历原始数据,只读不修改 for (int i = 0; i < dataModel.getRowCount(); i++) { Object typeObj = dataModel.getValueAt(i, 2); // 跳过Type & Size为空的行,不删除 if (typeObj == null) { continue; } String mark = (String) typeObj; if (mark.equals("R6")) { Object weightObj = dataModel.getValueAt(i, 3); if (weightObj != null) { BigDecimal weights = new BigDecimal(weightObj.toString()); tWeightRsix = tWeightRsix.add(weights).setScale(2, RoundingMode.HALF_UP); } } } // 添加汇总结果 summeryModel.addRow(new Object[]{"R6", tWeightRsix}); } });
2. 最佳实践:分离数据源与视图
如果后续需要频繁对数据进行操作,建议将原始数据存储在独立的数据源(比如自定义实体类列表)中,TableModel仅作为该数据源的视图。这样无论怎么处理数据,原始数据源都不会被破坏,TableModel也能随时从数据源刷新。
步骤示例
定义实体类存储每行数据
public class BarData { private String member; private String barMark; private String typeSize; private String weight; public BarData(String member, String barMark, String typeSize, String weight) { this.member = member; this.barMark = barMark; this.typeSize = typeSize; this.weight = weight; } // 省略getter和setter方法 public String getMember() { return member; } public String getBarMark() { return barMark; } public String getTypeSize() { return typeSize; } public String getWeight() { return weight; } }
在DataPanel中初始化数据源并绑定到TableModel
private List<BarData> originalData = new ArrayList<>(); public DataPanel() { // 初始化原始数据源 originalData.add(new BarData("Column", null, null, null)); originalData.add(new BarData(null, "01", "T20", "500")); originalData.add(new BarData(null, "01", "R6", "250")); originalData.add(new BarData(null, "02", "R6", "50")); originalData.add(new BarData("Slab", null, null, null)); originalData.add(new BarData(null, "03", "T10", "300")); originalData.add(new BarData(null, "04", "T10", "150")); // 将数据源转换为TableModel DefaultTableModel dataModel = new DefaultTableModel( new String[]{"Member", "Bar mark", "Type & Size", "Weight"}, 0 ); for (BarData data : originalData) { dataModel.addRow(new Object[]{ data.getMember(), data.getBarMark(), data.getTypeSize(), data.getWeight() }); } dataTable.setModel(dataModel); // 其他界面初始化代码... }
求和时直接遍历原始数据源
sumButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { DefaultTableModel summeryModel = (DefaultTableModel) summeryTable.getModel(); BigDecimal tWeightRsix = BigDecimal.ZERO; // 清空汇总表 int rowCount = summeryModel.getRowCount(); for (int j = rowCount - 1; j >= 0; j--) { summeryModel.removeRow(j); } // 遍历原始数据源,完全不影响TableModel for (BarData data : originalData) { String typeSize = data.getTypeSize(); if (typeSize == null) { continue; } if (typeSize.equals("R6")) { String weightStr = data.getWeight(); if (weightStr != null) { BigDecimal weights = new BigDecimal(weightStr); tWeightRsix = tWeightRsix.add(weights).setScale(2, RoundingMode.HALF_UP); } } } summeryModel.addRow(new Object[]{"R6", tWeightRsix}); } });
这种方式彻底分离了数据和视图,不仅能保证原始数据不被修改,也让数据操作逻辑更清晰,便于后续扩展。
内容的提问来源于stack exchange,提问作者tharinduhd
相关产品推荐
相关产品推荐

