是否有 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,但该异常是 notMap 接口中声明的. 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);
      }
    };