Question

I have a model class that stores keys and values:

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;
    }

}

I use this class to populate a JComboBox's Model:

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

I would like to refactor this logic to use a Java collection class (call it KeyValueCollection) that can support two objectives:

1) the KeyValueCollection can be used to populate the JComboBox's Model. Something like:

//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) I can use the KeyValueCollection to convert a key to a value:

//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();

In the .NET world, I would use the KeyedCollection class for this, but I'm not very familiar with Java.

Help is greatly appreciated.

Was it helpful?

Solution

You can use a custom class like this one (run main function to see its behavior) :

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);


    }


}

EDIT : to handle the selected item and keys, you may add these methods:

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

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

By default, values are ordered following the natural order of the keys (alphabetical order of the keys, here, because these are String). If you need an other ordering, add a java.util.Comparator to the TreeMap (see TreeMap documentation).

OTHER TIPS

The Map (implementation HashMap) is a Key-Value class.

It converts from key to value using the method #get.

There are also method to access all keys, all values and so on. So you should have no problem to fill a model with it.

It also contains a Key-Value pair that is called Map.Entry.

Your second requirement suggests that you want a Map, but ComboboxModel is a ListModel, which suggests that you'll want to be able to efficiently retrieve elements by "index".

I don't believe any of the standard collections can do this for you as simply as you'd like. You can either create a Map, and then copy the values to a separate List/ComboboxModel, or you could use something like IndexedList (a List implementation that maintains an index Map).

What about java.util.Map implementations?

with HashMap, for example, you can have:

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

However, you can't directly populate the JComboBox with the Map. You can add all keys to the JComboBox, and then get the corresponding values when needed. Adding can be done in many ways, two of which:

  • new JComboBox(map.keySet().toArray(new Object[]));
  • by a loop:

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

I think that a plain HashMap<Object,String> can address most of your needs:

// 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);

I use the following code:

/**
 * 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 $
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top