执行以下代码:
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);
我希望它会覆盖上一个条目,而不是添加另一个具有相同的、重复的键的条目。
3条答案
按热度按时间kcugc4gi1#
解释
你对结果的解释有点错误。你说是指纹
得出结论你有两把
Long
有价值的0
. 但这实际上是错误的。一个确实是一个Long
,来自这里:但另一个是
String
"0"
,在印刷品中,看起来是一样的。原始类型
您可能会感到困惑,为什么在您的
HashMap<Long, Block>
. 毕竟,它将键指定为Long
,而不是String
.问题在于:
这里发生的是你用
putAll
,但提供Map
. 注意,它没有泛型,它是原始类型。通过使用原始类型,您基本上牺牲了所有类型安全,并向java承诺“是的,是的,相信我,我知道我在做什么”。所以java只会相信你给了它一个
Map<Long, Block>
,但你实际上是在给它一个Map<String, String>
.堆污染
在java中,泛型在运行时被删除。这意味着,在通过编译之后,java对泛型一无所知,基本上允许您根据需要混合类型,它只是
Map<Object, Object>
“或多或少。所以你设法欺骗了javas安全的泛型类型系统,并注入了一个
String
变成只接受Long
把它当作钥匙。这就是所谓的堆污染。这也是为什么永远不要使用原始类型的主要原因。使用原始类型没有好处。堆污染的一个小例子:
注意事项
以下是有关此主题的更多信息:
java泛型类型擦除:何时发生,发生了什么?
堆污染
ukqbszuj2#
这里的问题正如@zabuza所指出的,您使用的是一个没有泛型的原始Map,泛型在运行时会被删除。试一下这个例子:
你会看到你的第一把钥匙是
Long
而第二个键是String
因为equals方法是在String
作为:equals总是返回false
00jrzges3#
我希望以下代码能消除您的疑虑:
输出此代码: