In map we have key, value pair.If we try to put same key in map 2 times it will produce a error.Now i want similar kind of behavior for value too.In short when i put a new key,value into the map both key and value should be unique, or else it should through an exception, How can i achive this?
Sounds like you want a Guava Bimap
.
A bimap (or "bidirectional map") is a map that preserves the uniqueness of its values as well as that of its keys. This constraint enables bimaps to support an "inverse view", which is another bimap containing the same entries as this bimap but with reversed keys and values.
(Guava is a great library in general, btw. Well worth using.)
Create a new class that contains a Map, plus a Set of values. On insert, if the value is in the set, throw an exception (or return false, or do nothing) otherwise add it to the map.
So you use the Map to enforce unique keys, and the Set to enforce unique Values.
If you want a bi-directional Map then just replace the set with a Map
You may want to subclass HashMap to create your own bimap:
Changed my mind - there's no need to subclass HashMap. Composition is a much cleaner approach, the following example decorates a concrete Map with a BiMap behavour.
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);
}
}
It's more code compared to subclassing an existing map, but it's worth it. Now we're completely independant of any existing Map implementation.
Before inserting a value into the map, do a check using Map.containsKey
and/or Map.containsValue
(not sure if you want to replace values for keys that are already present).
Edit: Wrong answer when having lots of inserts, still valid for small data sets.
16384 insertions of random Strings: map 12828 ms bimap 31 ms
1024 insertions of random Strings: 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();
}
}
Disclaimer: I would definitely go with Google Collections. It's probably the best-quality library I've ever used (It has great API, great code under the hood, and great documentation).
You can implement your own Map based on one of existing Map implementations:
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
}
}