是否有 Java HashMap 实现,其中键值在初始插入后无法更改?
Is there a Java HashMap implementation where key-values can't be changed after initial insertion?
我正在研究 this question on creating a HashTable<K,V>
as final
. I read this 答案,其中我们使用 Collections.unmodifiableMap
在散列 table 上放置一个不可修改的包装器。这让我想知道是否有一个 HashMap
实现或任何其他类似的结构,我可以在任何时候插入,但是一旦我插入了一个键和值,就不允许在同一个键上进行另一个插入。
例如:
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
我能想到的一个解决方案是:
if(test.get(1) == null) {
test.put(1, 50); //Similarly for all insertions
}
或者创建一个方法来执行此检查,例如:
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;
}
}
我也知道 putIfAbsent(K, V)
方法,但是如果有人试图 put()
已经存在的密钥的一些值,这也不会被禁止。
如果密钥已经存在,我提出的所有解决方案都不会阻止某人插入 HashMap,除非他们使用 putIfNotPresent()
自定义方法或 putIfAbsent
每次插入的方法。是否有这样的 HashMap
实现可用,一旦插入一个键和值,就不允许在同一个键上插入,即一旦一个键被插入一个值,它应该像键的值是 final
?
默认情况下没有这样的实现。但是,您可以使用 Guava 的 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);
}
}
我写了一个 putAll
的默认实现,但是基于状态抛出异常取决于你,你可能想改变它以满足你的需要:要么根本不插入,要么插入直到被欺骗找到了。
此外,请注意,我选择在发现欺骗时抛出 IllegalStateException
,但该异常是 not 在 Map
接口中声明的. Map
接口中声明的异常不应根据状态抛出。所以你最好把它记录下来,并说这个实现在这方面破坏了 Map 接口。
您可以像这样覆盖 .put()
:
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);
}
};
我正在研究 this question on creating a HashTable<K,V>
as final
. I read this 答案,其中我们使用 Collections.unmodifiableMap
在散列 table 上放置一个不可修改的包装器。这让我想知道是否有一个 HashMap
实现或任何其他类似的结构,我可以在任何时候插入,但是一旦我插入了一个键和值,就不允许在同一个键上进行另一个插入。
例如:
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
我能想到的一个解决方案是:
if(test.get(1) == null) {
test.put(1, 50); //Similarly for all insertions
}
或者创建一个方法来执行此检查,例如:
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;
}
}
我也知道 putIfAbsent(K, V)
方法,但是如果有人试图 put()
已经存在的密钥的一些值,这也不会被禁止。
如果密钥已经存在,我提出的所有解决方案都不会阻止某人插入 HashMap,除非他们使用 putIfNotPresent()
自定义方法或 putIfAbsent
每次插入的方法。是否有这样的 HashMap
实现可用,一旦插入一个键和值,就不允许在同一个键上插入,即一旦一个键被插入一个值,它应该像键的值是 final
?
默认情况下没有这样的实现。但是,您可以使用 Guava 的 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);
}
}
我写了一个 putAll
的默认实现,但是基于状态抛出异常取决于你,你可能想改变它以满足你的需要:要么根本不插入,要么插入直到被欺骗找到了。
此外,请注意,我选择在发现欺骗时抛出 IllegalStateException
,但该异常是 not 在 Map
接口中声明的. Map
接口中声明的异常不应根据状态抛出。所以你最好把它记录下来,并说这个实现在这方面破坏了 Map 接口。
您可以像这样覆盖 .put()
:
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);
}
};