WeakHashMap的意外行为,Map为键,修改键后返回null值

时间:2017-09-27 09:13:16

标签: java hashmap hashcode

我们需要缓存一些有关某些对象的信息,因此我们正在使用java.util.WeakHashMap。如果密钥是java.util.HashMap,我们会看到意外的行为。

示例:

WeakHashMap<Object, Object> whm = new WeakHashMap<>();
Map<String, String> map = new HashMap<>();
whm.put(map, "map");
System.out.println(map + ": " + whm.get(map) + " " + whm + " " + whm.containsKey(map));

map.put("key", "value");
System.out.println(map + ": " + whm.get(map) + " " + whm + " " + whm.containsKey(map));
System.out.println(map.hashCode());
System.out.println(whm.entrySet().stream().map(e -> e.getKey().hashCode()).collect(Collectors.toList()));
System.out.println(whm.entrySet().stream().map(e -> e.getKey() == map).collect(Collectors.toList()));

输出是:

{}: map {{}=map} true
{key=value}: null {{key=value}=map} false
112004910
[112004910]
[true]

调用whm.get(map)后为什么null whm.put(map, "map")

java.util.HashSet ...

的结果相同

对于AtomicInteger,它按预期工作:

WeakHashMap<Object, Object> whm = new WeakHashMap<>();
AtomicInteger integer = new AtomicInteger(0);
whm.put(integer, "integer");
System.out.println(integer + ": " + whm.get(integer) + " " + whm + " " + whm.containsKey(integer));

integer.set(1);
System.out.println(integer + ": " + whm.get(integer) + " " + whm + " " + whm.containsKey(integer));

结果:

0: integer {0=integer} true
1: integer {1=integer} true

2 个答案:

答案 0 :(得分:8)

这与地图无关,而且与你修改地图键有关,这基本上是你应该避免做的事情。通过向地图添加条目,您将更改其哈希码。这很容易证明:

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

public class Test {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        System.out.println(map.hashCode());
        map.put("key", "value");
        System.out.println(map.hashCode());        
    }
}

此时尝试获取条目将失败,因为其哈希码不再与插入的哈希码匹配。

AtomicInteger不会覆盖equalshashCode,因此您会获得对象标识相等 - 当您调用set时,其哈希码不会更改。< / p>

答案 1 :(得分:0)

我的解决方案是使用相同的方式添加WeakMap,在这种情况下可以正常工作:

import java.util.IdentityHashMap;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * {@link WeakHashMap} also using equality to support mutable keys with changing {@link Object#hashCode()} like {@link IdentityHashMap}. In case of equality
 * checking the performance will be {@code O(n)}. <b>Currently just {@link Map#get(Object)} and {@link Map#containsKey(Object)} are supported for equality.</b>
 *
 * @author Andre Schulz
 */
public class WeakIdentityMap<K, V> extends WeakHashMap<K, V> {

    @Override
    public boolean containsKey(Object key) {
        boolean result = super.containsKey(key);

        if (result) {
            return result;
        }

        for (Map.Entry<K, V> entry : super.entrySet()) {
            if (entry.getKey() == key) {
                return true;
            }
        }

        return false;
    }

    @Override
    public V get(Object key) {
        V value = super.get(key);

        if (value != null) {
            return value;
        }

        for (Map.Entry<K, V> entry : super.entrySet()) {
            if (entry.getKey() == key) {
                return entry.getValue();
            }
        }

        return null;
    }
}