【问题标题】:Need flexible Java key/value collection class for JComboBoxJComboBox 需要灵活的 Java 键/值集合类
【发布时间】:2010-01-19 16:42:43
【问题描述】:

我有一个存储键和值的模型类:

public class KeyValue {

    private Object key;
    private String value;

    KeyValue () {
    }

    KeyValue (Object key, String value) {
        this.key=key;
        this.value=value;
    }

    public Object getKey() {
        return this.key;
    }
    public void setKey(Object key) {
        this.key=key;
    }

    public String getValue() {
        return this.value;
    }
    public void setValue(String value) {
        this.value=value;
    }

    @Override
    public String toString() {
        return this.value;
    }

}

我使用这个类来填充JComboBox 的模型:

for (int i = 0; i < universes.length; i++) {
    ComboBox_Universes.addItem(new KeyValue(infoObject.ID,infoObject.title));
}

我想重构这个逻辑以使用可以支持两个目标的 Java 集合类(称为 KeyValueCollection):

1) KeyValueCollection 可用于填充JComboBox 的模型。比如:

//get a KeyValueCollection filled with data from helper class
KeyValueCollection universeCollection = Repository.getUniverseCollection();

//use this collection as the JComboBox's model
ComboBox_Universes.setModel(universeCollection);

2) 我可以使用KeyValueCollection 将键转换为值:

//ID retrieve from another control
int universeID = (int)this.Table_Values.getModel().getValueAt(row, COLUMN_ID);

//convert ID to name
String universeName = universeCollection.get(universeID).getValue();

在 .NET 世界中,我会为此使用 KeyedCollection 类,但我对 Java 不是很熟悉。

非常感谢您的帮助。

【问题讨论】:

    标签: java swing collections jcombobox


    【解决方案1】:

    您可以使用像这样的自定义类(运行 main 函数以查看其行为):

    import java.awt.BorderLayout;
    import java.awt.Component;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.TreeMap;
    
    import javax.swing.AbstractListModel;
    import javax.swing.ComboBoxModel;
    import javax.swing.DefaultListCellRenderer;
    import javax.swing.JComboBox;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JList;
    import javax.swing.JPanel;
    
    public class KeyValueComboboxModel extends AbstractListModel implements ComboBoxModel, Map<String, String> {
    
        private TreeMap<String,String> values = new TreeMap<String,String>();
    
        private Map.Entry<String, String> selectedItem = null;
    
        public Object getSelectedItem() {
            return selectedItem;
        }
    
        public void setSelectedItem(Object anItem) {
            this.selectedItem = (java.util.Map.Entry<String, String>) anItem;
            fireContentsChanged(this, -1, -1);
        }
    
        public Object getElementAt(int index) {
            List<Map.Entry<String, String>> list = new ArrayList<Map.Entry<String, String>>(values.entrySet());
            return list.get(index);
        }
    
    
    
        public int getSize() {
            return values.size();
        }
    
        public void clear() {
            values.clear();
        }
    
        public boolean containsKey(Object key) {
            return values.containsKey(key);
        }
    
        public boolean containsValue(Object value) {
            return values.containsValue(value);
        }
    
        public Set<java.util.Map.Entry<String, String>> entrySet() {
            return values.entrySet();
        }
    
        public String get(Object key) {
            return values.get(key);
        }
    
        public Set<String> keySet() {
            return values.keySet();
        }
    
        public String put(String key, String value) {
            return values.put(key, value);
        }
    
        public String remove(Object key) {
            return values.remove(key);
        }
    
        public int size() {
            return values.size();
        }
    
        public Collection<String> values() {
            return values.values();
        }
    
        public boolean isEmpty() {
            return values.isEmpty();
        }
    
        public void putAll(Map<? extends String, ? extends String> m) {
            values.putAll(m);
        }
    
    
        private static String entryToString(Map.Entry<String, String> entry) {
            String str = "" + entry.getKey() + "->" + entry.getValue();
            return str;
        }
    
        public static void main(String[] args) {
    
            Map<String,String> map= new HashMap<String,String>(){{
                put("1","blue");
                put("2","red");
                put("3","white");
                put("4","black");
            }};
    
            JFrame f = new JFrame();
            f.setContentPane(new JPanel(new BorderLayout()));
    
            KeyValueComboboxModel model = new KeyValueComboboxModel();
            model.putAll(map);
    
            final JComboBox combo = new JComboBox(model);
            combo.setRenderer(new DefaultListCellRenderer(){
    
                @Override
                public Component getListCellRendererComponent(JList list, Object value, int index,
                        boolean isSelected, boolean cellHasFocus) {
                    if(value instanceof Map.Entry){
                        Map.Entry<String,String> entry = (java.util.Map.Entry<String, String>) value;
                        String str = entryToString(entry);
                        return super.getListCellRendererComponent(list, str, index, isSelected, cellHasFocus);
                    }
                    return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                }
    
            });
    
            final JLabel lab = new JLabel("Nothing selected");
    
            combo.addActionListener(new ActionListener(){
    
                public void actionPerformed(ActionEvent e) {
                    if(combo.getSelectedItem()!=null){
                        lab.setText(entryToString((java.util.Map.Entry<String, String>) combo.getSelectedItem()));
                    } else {
                        lab.setText("");
                    }
    
                }
    
            });
    
            f.getContentPane().add(combo,BorderLayout.CENTER);
            f.getContentPane().add(lab,BorderLayout.SOUTH);
    
            f.setSize(300,80);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setLocationRelativeTo(null);
            f.setVisible(true);
    
    
        }
    
    
    }
    

    编辑:要处理选定的项目和键,您可以添加以下方法:

    public void setSelectedKey(String key){
        selectedItem = values.ceilingEntry(key);
        setSelectedItem(key);
    }
    
    public void setSelectedItem(String key, String value){
        values.put(key, value);
        setSelectedKey(key);
    }
    

    默认情况下,值按照键的自然顺序排序(这里是键的字母顺序,因为它们是String)。如果您需要其他排序,请将 java.util.Comparator 添加到 TreeMap(请参阅 TreeMap 文档)。

    【讨论】:

    • 三个问题: 1. 设置SelectedItem的语法是什么? Foo 的部分是 [foo]ling 我......对不起。 Map.Entry item = new Foo("2","re​​d");模型.setSelectedItem(项目); 2. 如果我想要一个 setSelectedKey() 方法,那么我需要从 TreeMap 中找到正确的 Map.Entry,然后调用 setSelectedItem,对吗? 3. 理想情况下,组合框的值将按字母顺序排序。我假设我需要对 HashMap 的值进行排序。还有其他更简单的方法吗?
    • 不应该将 setSelectedKey() 方法读取为: public void setSelectedKey(String key) { //调用设置变量并触发事件的方法 setSelectedItem( values.ceilingEntry(key) ); }
    • Map&lt;String,String&gt; map= new HashMap&lt;String,String&gt;(){{ put("1","blue"); put("2","red"); put("3","white"); put("4","black"); }}; 是向地图添加条目的一种非常糟糕的方法,因为它为此目的创建了一个匿名类。
    【解决方案2】:

    Map(实现HashMap)是一个Key-Value类。

    它使用 #get 方法从键转换为值。

    还有访问所有键、所有值等的方法。所以用它填充模型应该没有问题。

    它还包含一个名为 Map.Entry 的键值对。

    【讨论】:

      【解决方案3】:

      您的第二个要求表明您需要Map,但ComboboxModelListModel,这表明您希望能够通过“索引”有效地检索元素。

      我不相信任何标准系列都能如您所愿地为您做到这一点。您可以创建一个 Map,然后将值复制到单独的 List/ComboboxModel,或者您可以使用类似 IndexedList(维护索引 Map 的 List 实现)之类的东西。

      【讨论】:

        【解决方案4】:

        java.util.Map 的实现呢?

        HashMap 为例,您可以:

        Map<Object, String> map = new HashMap<Object, String>();
        map.put(key, value);
        Object value = map.get(key);
        

        但是,您不能直接使用Map 填充JComboBox。您可以将所有键添加到JComboBox,然后在需要时获取相应的值。添加可以通过多种方式完成,其中两种:

        • new JComboBox(map.keySet().toArray(new Object[]));
        • 循环:

          for (Object key : map.keySet() {
              comboBox.addItem(key);
          }
          

        【讨论】:

          【解决方案5】:

          我认为简单的HashMap&lt;Object,String&gt; 可以满足您的大部分需求:

          // Build the map
          Map<Object,String> map = new HashMap<Object,String>();
          for(InfoObject io : universes) 
             map.put(io.ID,io.title);
          
          
          // Populate the ComboBox
          for(String s : map.values())
             ComboBox_Universes.addItem(s);
          
          
          // Convert ID to name
          int universeID = (int)this.Table_Values.getModel().getValueAt(row, COLUMN_ID);
          String universeName = map.get(universeID);
          

          【讨论】:

            【解决方案6】:

            我使用以下代码:

            /**
             * This class is slightly modified version of the Pair class from this thread:
             * http://stackoverflow.com/questions/156275/what-is-the-equivalent-of-the-c-pairl-r-in-java
             * As suggested in the thread above, I have made first & second to be final members.
             * I have made it into an Map.Entry<K,V> type, so it is suitable to be an element
             * of any Java Hash map...
             *
             * @author Dejan Lekic - http://dejan.lekic.org
             */
            public class Pair<KeyT, ValueT> implements Map.Entry<KeyT, ValueT> {
            
                protected KeyT first;
                protected ValueT second;
            
                public Pair(final KeyT argFirst, final ValueT argSecond) {
                    super();
                    this.first = argFirst;
                    this.second = argSecond;
                }
            
                @Override
                public int hashCode() {
                    int hashFirst = (first != null) ? first.hashCode() : 0;
                    int hashSecond = (second != null) ? second.hashCode() : 0;
            
                    return (hashFirst + hashSecond) * hashSecond + hashFirst;
                }
            
                @Override
                public boolean equals(final Object other) {
                    if (other instanceof Pair) {
                        Pair otherPair = (Pair) other;
                        return ((this.first == otherPair.first
                                || (this.first != null && otherPair.first != null
                                && this.first.equals(otherPair.first)))
                                && (this.second == otherPair.second
                                || (this.second != null && otherPair.second != null
                                && this.second.equals(otherPair.second))));
                    } // if
                    return false;
                } // equals() method
            
                @Override
                public String toString() {
                    // previously we used " - " as a separator. Now we will use the 0x1f character, called the UNIT
                    // SEPARATOR to separate two fields in a String object. See the Sise class for more information.
                    return first + "\u001f" + second;
                }
            
                public KeyT getFirst() {
                    return first;
                }
            
                public void setFirst(final KeyT argFirst) {
                    this.first = argFirst;
                }
            
                public ValueT getSecond() {
                    return second;
                }
            
                public void setSecond(final ValueT argSecond) {
                    this.second = argSecond;
                }
            
                @Override
                public ValueT setValue(final ValueT argNewValue) {
                    ValueT oldValue = second;
                    second = argNewValue;
                    return oldValue;
                }
            
                @Override
                public ValueT getValue() {
                    return second;
                }
            
                @Override
                public KeyT getKey() {
                    return first;
                }
            } // Pair class
            
            // $Id: Pair.java 149 2012-01-13 12:30:59Z dejan $
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-03-25
              • 2015-08-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多