映射所需的行为

时间:2010-08-13 13:22:04

标签: java map

在地图中我们有键值对。如果我们尝试将相同的键放在地图中2次就会产生错误。现在我也想要类似于行为的值。简而言之,当我放一个新键时,值在映射中,键和值都应该是唯一的,否则它应该通过异常, 我怎么能做到这一点?

5 个答案:

答案 0 :(得分:5)

听起来像是想要Guava Bimap

  

bimap(或“双向地图”)是一个   保留唯一性的地图   它的价值以及它的价值   键。此约束启用了bimaps   支持“逆视图”,即   另一个含有相同的bimap   作为这个bimap的条目,但有   反转键和值。

(一般来说,番石榴是一个很棒的图书馆,顺便说一句。值得使用。)

答案 1 :(得分:3)

您可能希望将HashMap子类化以创建自己的bimap:

改变主意 - 没有必要继承HashMap。 合成是一种更清晰的方法,下面的示例装饰具有BiMap行为的具体地图。

public class BiMap<K, V> implements Map<K, V> {

    private Map<K, V> inner;
    private Set<V> values;

    public BiMap(Map<K, V> map) {
        if (map == null || !map.isEmpty())
            throw new IllegalArgumentException("This implementation requires an empty map");
        inner = map;
        values = new HashSet<V>();
    }

    public boolean containsKey(Object key) { return inner.containsKey(key); }
    public boolean containsValue(Object value) { return values.contains(value); }
    public Set<java.util.Map.Entry<K, V>> entrySet() { return inner.entrySet(); }
    public V get(Object key) { return inner.get(key); }
    public boolean isEmpty() { return inner.isEmpty(); }
    public Set<K> keySet() { return inner.keySet(); }
    public int size() { return inner.size(); }
    public Set<V> values() { return Collections.unmodifiableSet(values); }
    public boolean equals(Object obj) { return inner.equals(obj); }
    public int hashCode() { return inner.hashCode(); }
    public String toString() { return inner.toString(); }

    public void clear() {
        values.clear();
        inner.clear();
    }

    public V put(K key, V value) {
        if (values.contains(value)) {
            throw new IllegalArgumentException("Value already exists in map");
        }
        values.add(value);
        return inner.put(key, value);
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    public V remove(Object key) {
        values.remove(key);
        return inner.remove(key);
    }    
}

与子类化现有地图相比,它的代码更多,但值得。现在我们完全独立于任何现有的Map实现。

答案 2 :(得分:2)

创建一个包含Map的新类,以及一组值。在插入时,如果值在集合中,则抛出异常(或返回false,或不执行任何操作),否则将其添加到地图中。

因此,您可以使用Map强制使用唯一键,并使用Set来强制使用唯一值。

如果你想要一个双向地图,那么只需用地图

替换这个集合

答案 3 :(得分:1)

免责声明:我肯定会使用Google Collections。它可能是我用过的最优质的库(它有很棒的API,很棒的代码和很棒的文档)。

您可以根据现有的Map实现之一实现自己的Map:

public class UniqueValuesMap<K,V> implements Map<K,V> {

   private final Map<K, V> innerMap = new HashMap<K, V> ();

   public int size() {
       return innerMap.size();
   }

   ...
   //all other methods

   public V put(K key, V value) {
      if (innerMap.values().contains (value) {
          throw new IllegalArgumentException ("some msg");
      }
      return innerMap.put (key, value);
   }

   public void putAll(Map<? extends K, ? extends V> m) {
       // implementation
   }

}

答案 4 :(得分:0)

在将值插入地图之前,请使用Map.containsKey和/或Map.containsValue进行检查(不确定是否要替换已存在的键的值)。

编辑:有大量插入时的错误答案,仍然适用于小型数据集。

  

16384随机字符串的插入:   地图12828 ms bimap 31 ms

     

1024个随机字符串插入:map   32 ms bimap 15 ms

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import com.google.common.collect.HashBiMap;

public class InsertTest
{
    private Map<Integer, String> map = new HashMap<Integer, String>();
    private Map<Integer, String> bimap = HashBiMap.create();

    public void run()
    {
        String[] values = new String[1024];
            Random r = new Random();
        for (int i = 0; i < values.length; i++)
        {
          values[i] = Long.toString(Math.abs(r.nextLong()), 36);
        }
        test("map", map, values);
        test("bimap", bimap, values);
    }

    private static void test(String name, Map<Integer, String> map, String[] values)
    {
        int key = 0;
        long start = System.currentTimeMillis();
        for (String value : values)
        {
            if (!map.containsValue(value))
            {
                map.put(key++, value);
            }
        }
        long end = System.currentTimeMillis();
        System.out.println(name + " " + (end - start));
    }

    public static void main(String[] args)
    {
        new InsertTest().run();
    }
}