更新JComboBox模型时保留当前选中项的优化方案咨询
优化JComboBox动态更新的实现方案
你的核心疑问答案很明确:完全不需要直接操作JComboBox组件,仅操作DefaultComboBoxModel就足够了。Swing遵循MVC设计,视图(JComboBox)会监听模型的变化,自动同步选中状态和列表内容,所以cbxCars.setSelectedIndex这行代码完全是冗余的,可以删掉。
下面是针对代码的具体优化点和完整优化后的版本:
关键优化点
1. 修复CBItem的equals与hashCode方法
当前equals仅比较description,不符合Java规范(重写equals必须重写hashCode),且如果存在不同ID但相同描述的项会导致判断错误。建议同时比较id和description:
@Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof CBItem)) return false; CBItem cbItem = (CBItem) o; return id == cbItem.id && description.equals(cbItem.description); } @Override public int hashCode() { return Objects.hash(id, description); }
2. 简化SwingWorker逻辑
不需要在类级别定义lstNewCars,直接在doInBackground中创建并返回,代码更简洁:
protected ArrayList<CBItem> doInBackground() { Random rand = new Random(); int lastCar = 4 + rand.nextInt(3); ArrayList<CBItem> lstNewCars = new ArrayList<>(); for (int c = 0; c < lastCar; c++) { lstNewCars.add(new CBItem(c, arrCarNames[c])); } return lstNewCars; }
3. 精简done方法,移除冗余操作
- 删除
cbxCars.setSelectedIndex,仅通过模型设置选中项即可 - 优化新旧列表的判断逻辑,先判断长度是否一致,提升效率
- 复制新列表到
lstCurrentCars,避免引用传递导致的意外修改
优化后的done方法:
protected void done() { try { ArrayList<CBItem> lstNewCars = get(); // 长度不同直接更新,无需后续比较 if (lstNewCars.size() != lstCurrentCars.size() || !lstNewCars.equals(lstCurrentCars)) { // 保存当前选中项 CBItem currentSelection = (CBItem) mdlCars.getSelectedItem(); // 更新模型内容 mdlCars.removeAllElements(); mdlCars.addAll(lstNewCars); // 若选中项存在于新列表,恢复选中状态 if (currentSelection != null && lstNewCars.contains(currentSelection)) { mdlCars.setSelectedItem(currentSelection); } // 更新当前列表(用副本避免引用问题) lstCurrentCars = new ArrayList<>(lstNewCars); } } catch (Exception e) { e.printStackTrace(); } }
4. 其他细节调整
- 修正Timer的注释(原注释写5秒,实际是3秒,保持一致)
- 用lambda简化Timer的ActionListener代码,更简洁
完整优化后的代码
import java.awt.BorderLayout; import java.awt.EventQueue; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Random; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingWorker; import javax.swing.Timer; class CBItem { private int id; private String description; public CBItem(int id, String description) { this.id = id; this.description = description; } public int getId() { return id; } public String getDescription() { return description; } @Override public String toString() { return description; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof CBItem)) return false; CBItem cbItem = (CBItem) o; return id == cbItem.id && description.equals(cbItem.description); } @Override public int hashCode() { return Objects.hash(id, description); } } public class JComboBoxFiller extends JFrame { private static final long serialVersionUID = 1L; JPanel pnlMain; DefaultComboBoxModel<CBItem> mdlCars = new DefaultComboBoxModel<>(); List<CBItem> lstCurrentCars = new ArrayList<>(); JComboBox<CBItem> cbxCars; final String[] arrCarNames = new String[]{"Audi", "BMW", "Chevrolet", "Dodge", "Ford", "Hyundai", "Jaguar"}; public JComboBoxFiller() { setTitle("ComboBox Filler Demo"); setBounds(100, 100, 400, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pnlMain = new JPanel(new BorderLayout()); setContentPane(pnlMain); cbxCars = new JComboBox<>(mdlCars); JPanel pnlCenter = new JPanel(); pnlCenter.add(cbxCars); pnlMain.add(pnlCenter, BorderLayout.CENTER); setVisible(true); // 每3秒重新加载车型列表 Timer timer = new Timer(3000, e -> new RepopulateCarsWorker().execute()); timer.start(); } private class RepopulateCarsWorker extends SwingWorker<ArrayList<CBItem>, Void> { @Override protected ArrayList<CBItem> doInBackground() { Random rand = new Random(); int lastCar = 4 + rand.nextInt(3); ArrayList<CBItem> lstNewCars = new ArrayList<>(); for (int c = 0; c < lastCar; c++) { lstNewCars.add(new CBItem(c, arrCarNames[c])); } return lstNewCars; } @Override protected void done() { try { ArrayList<CBItem> lstNewCars = get(); if (lstNewCars.size() != lstCurrentCars.size() || !lstNewCars.equals(lstCurrentCars)) { CBItem currentSelection = (CBItem) mdlCars.getSelectedItem(); mdlCars.removeAllElements(); mdlCars.addAll(lstNewCars); if (currentSelection != null && lstNewCars.contains(currentSelection)) { mdlCars.setSelectedItem(currentSelection); } lstCurrentCars = new ArrayList<>(lstNewCars); } } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { EventQueue.invokeLater(() -> { try { new JComboBoxFiller().setVisible(true); } catch (Exception e) { e.printStackTrace(); } }); } }
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

