I was going through this question on creating a HashTable<K,V> as final. I read this answer where we use Collections.unmodifiableMap to put an un-modifiable wrapper over the hash table. This got me wondering if there is a HashMap implementation or any other similar structure, where I can insert whenever I want but once I've inserted a key and value, another insertion on the same key is not allowed.
For eg:
private final HashMap<Integer, Integer> test = new HashMap<>();
test.put(1, 100);
test.put(2, 200);
/*
* Other insertions
*/
test.put(1, 50); //This should not be allowed
One solution which I can think of is to do:
if(test.get(1) == null) {
test.put(1, 50); //Similarly for all insertions
}
or to create a method which will do this check, like:
private void putIfNotPresent(int key, int value) {
if(test.get(key) == null) { //Considering that null will be returned if there is no mapping available for the key
test.put(key, value);
} else {
throw new UnsupportedOperationException;
}
}
I'm also aware of the putIfAbsent(K, V) method, but this also will not disallow if someone tries to put() some value for an already existing key.
All the solutions which I've come up with won't stop someone from inserting into the HashMap if the key is already present, unless they use the putIfNotPresent() custom method or the putIfAbsent method for every insertion. Is there such a HashMap implementation available where once a key and value is inserted, insertion on the same key is disallowed i.e. once a key is inserted with a value, it should act as if the key's value is final?
There is no such implementation by default. However, you can easily make one using Guava's ForwardingMap:
class FailOnDupeMap<K,V> extends ForwardingMap<K,V> {
private final Map<K,V> delegate = new HashMap<>();
protected Map<K, V> delegate() { return delegate; }
@Override public V put(K key, V value) {
var result = delegate().putIfAbsent(key, value);
checkState(result == null, "a value already exist for key %s", key);
return null;
}
@Override public void putAll(Map<? extends K, ? extends V> map) {
standardPutAll(map);
}
}
I wrote a default implementation of putAll, but throwing exception based on state depends so much on you that you probably want to change it to suit your needs: either no insertion at all or insert until a dupe is found.
Also, please note that I chose to throw an IllegalStateException when a dupe is found, but that exception is not declared in the Map interface. No exception declared in the Map interface should be thrown based on state. So you'd better well document it and say that this implementation breaks the Map interface in that regards.
You can override the .put() something like this:
HashMap<Integer, Integer> test =
new HashMap<Integer, Integer>() {
@Override
public Integer put(Integer key, Integer value) {
// handle do not insert logic here
if (this.containsKey(key)) {
throw new UnsupportedOperationException();
}
// key not in map, insert
return super.put(key, value);
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With