JavaHashMap似乎允许2个重复的键

ufj5ltwl  于 2021-07-12  发布在  Java
关注(0)|答案(3)|浏览(313)

执行以下代码:

public class Main {

    public static void main(String[] args) {
        Util util = new Util();
        util.addBlock(1);
        util.addBlocks(util.getBlocks());
    }

}

public class Util {

    public static HashMap<Long, Integer> blockMap = new HashMap<>();
    private Gson gson = new Gson();

    public void addBlocks(String json){
        Map map = gson.fromJson(json, Map.class);
        blockMap.putAll(map);
        System.out.println(blockMap.keySet());
    }

    public void addBlock(int i){
        blockMap.put(0L, i);
    }

    public String getBlocks(){
        return gson.toJson(blockMap);
    }
}

我得到输出

[0, 0]

从印刷品上看 System.out.println(blockMap.keySet()); .
所以,出于某种原因,我有一个 Map<Long, Integer> 包含两个 Long 有价值的人 0 作为关键。和一个键集 Set<Long> 有两个 0 s。但是map和set不允许重复键,这怎么可能呢?
代码首先向Map添加一个简单条目:

blockMap.put(0L, i);

然后,我添加另一个条目,使用gson将Map转换为json字符串,然后再返回到Map:

gson.toJson(blockMap);
...
Map map = gson.fromJson(json, Map.class);
blockMap.putAll(map);

我希望它会覆盖上一个条目,而不是添加另一个具有相同的、重复的键的条目。

kcugc4gi

kcugc4gi1#

解释

你对结果的解释有点错误。你说是指纹

[0, 0]

得出结论你有两把 Long 有价值的 0 . 但这实际上是错误的。一个确实是一个 Long ,来自这里:

blockMap.put(0L, i);

但另一个是
String "0" ,在印刷品中,看起来是一样的。

原始类型

您可能会感到困惑,为什么在您的 HashMap<Long, Block> . 毕竟,它将键指定为 Long ,而不是 String .
问题在于:

Map map = gson.fromJson(json, Map.class);
blockMap.putAll(map);

这里发生的是你用 putAll ,但提供 Map . 注意,它没有泛型,它是原始类型。
通过使用原始类型,您基本上牺牲了所有类型安全,并向java承诺“是的,是的,相信我,我知道我在做什么”。所以java只会相信你给了它一个 Map<Long, Block> ,但你实际上是在给它一个 Map<String, String> .

堆污染

在java中,泛型在运行时被删除。这意味着,在通过编译之后,java对泛型一无所知,基本上允许您根据需要混合类型,它只是 Map<Object, Object> “或多或少。
所以你设法欺骗了javas安全的泛型类型系统,并注入了一个 String 变成只接受 Long 把它当作钥匙。这就是所谓的堆污染。这也是为什么永远不要使用原始类型的主要原因。使用原始类型没有好处。
堆污染的一个小例子:

List<Dog> dogs = new ArrayList<>(); // safe collection
dogs.add(new Dog());

// dogs.add(new Cat()); // not allowed, dogs is safe

List raw = dogs; // raw handle to safe collection
raw.add(new Cat()); // works! heap pollution

// compiles, but throws at run-time,
// since it is actually a cat, not a dog
Dog dog = dogs.get(1);

注意事项

以下是有关此主题的更多信息:
java泛型类型擦除:何时发生,发生了什么?
堆污染

ukqbszuj

ukqbszuj2#

这里的问题正如@zabuza所指出的,您使用的是一个没有泛型的原始Map,泛型在运行时会被删除。试一下这个例子:

for (Object key: Util.blockMap.keySet()) {
    System.out.println("key: " + key + " type: " + key.getClass());
}

你会看到你的第一把钥匙是 Long 而第二个键是 String 因为equals方法是在 String 作为:

if (anObject instanceof String) {
    // ...
}
return false;

equals总是返回false

00jrzges

00jrzges3#

我希望以下代码能消除您的疑虑:

public void addBlocks(String json) {
    Map map = gson.fromJson(json, Map.class);
    blockMap.putAll(map);
    System.out.println(blockMap.keySet());
    Iterator itr=blockMap.keySet().iterator();
    while(itr.hasNext()){
        System.out.println(itr.next().getClass());
    }
}

输出此代码:

[0, 0]
class java.lang.Long
class java.lang.String

相关问题