这里是无意中写出来的一个bug,感觉蛮有探索价值,就写了出来。
代码如下,有兴趣的可以先看一下,这个代码有个bug,看看你们找的出来不。
private static void deleteItemInOutReview(List<SupplierBillDifferItem> stockins,List<SupplierBillDifferItem> deleteDiffItems){
// 这里是个map用于存放对象,key为对象的几个属性拼接而成
Map<String, SupplierBillDifferItem> map = new HashMap<String, SupplierBillDifferItem>();
for (SupplierBillDifferItem item : stockins){
if (item.getStatus() == 1){
StringBuilder groupbyString = getStringBuilder(item);
map.put(groupbyString.toString(), item);
}
}
// 这里是个迭代器,这里有个bug
for (Iterator<SupplierBillDifferItem> ite = stockins.iterator(); ite.hasNext();) {
SupplierBillDifferItem item = ite.next();
if (item.getStatus() == 0){
StringBuilder groupbyString = getStringBuilder(item);
if (map.get(groupbyString) == null){
continue;
}
if (item.getId() == null || item.getId() == 0L){
ite.remove();
}else{
ite.remove();
deleteDiffItems.add(item);
}
}
}
}
String不可改变,StringBuilder ,StringBuffer可以改变通过.append方法扩展,String类型重写了equals方法和hashCode方法,所以它可以保证hashMap中的key通过这两个方法保证唯一性,StringBuilder没有重写quals方法和hashCode方法,所以StringBuilder作为key无法保证key唯一。
并且Map中set方法被泛型化了,而get方法没有被泛型化。get(Object key),所以如果像我代码中不相信get中的key写错了,但是编译器是可以通过的,map中找不到就会返回null,就会导致系统bug,导致数据错乱。就为什么get方法没有实现泛型化,stackoverflow上有一个回答概的意思就是说如果采用范型作为get
的参数并不能很好的体现出map
的精神,因为key是否相等,取决于key1.equals(key2)
是否成立,这里并不应该限制类型。
在sof上的另外一篇文章也阐述了这个问题,并引用了Josh Bloch的一段话:
Josh Bloch says (6:41) that they attempted to generify the get method of Map, remove method and some other, but "it simply didn't work". There are too many reasonable programs that could not be generified if you only allow the generic type of the collection as parameter type. The example given by him is an intersection of a List of Numbers and a List of Longs.
大概意思就是说我们曾经尝试过把get
方法范型化,但是它挂了,有很多很多原因使他不能被范型化,比如你用List<Number>
做key,但却想用List<Long>
来get
。