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

如何在保留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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 18:57:07