背景:读入文件数据,使用hashMap嵌套存储,出现key重复
问题如下:
文件格式使用csv格式,逗号分隔
数据如下:
源码如下:
package fileToData;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Created by wang on 2019/01/11.
*/
public class readFile {
private String filepath = "C:\\Users\\wang\\Desktop\\ceshi.csv";
private String division = ",";
private Map<String, Map<String, Integer>> uMap;
public readFile() {
uMap = new LinkedHashMap<>();
}
public void initMaps() throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(filepath));
String line;
while ((line = reader.readLine()) != null) {
String[] split = line.split(division);
//添加数据
if (!uMap.containsKey(split[0])) {
Map<String, Integer> tempMap = new LinkedHashMap<>();
tempMap.put(split[1], Integer.parseInt(split[3]));
uMap.put(split[0], tempMap);
} else {
//添加重复key数据
if (!uMap.get(split[0]).containsKey(split[1])) {
uMap.get(split[0]).put(split[1], Integer.parseInt(split[3]));
}
}
}
//关闭文件流
reader.close();
}
}
问题分析:
经过debug追踪查看发现两个字符串"1"的hash值不一样
如果key相同,但是hashcode不同,那么key不会被覆盖
如果key相同,但是hashcode相同,那么key会被覆盖
为什么会多了个字符"\uFEFF" ,hash值为65279呢?
可能问题出现在文件中,导致文件头有特殊字符,但我明明就是用的.csv格式
经过排查发现存在两种格式的csv:
问题是因为使用了上面utf-8的那个格式,正确的是下面那个
结论如下:
原因就是在文件头中的BOM标记造成,虽然文件内容看上去是正常的。
解决方法就是用文本编辑器将该文件重新另存为“无BOM标记”的文件即可。
以下是摘自网友的优秀回答:
理解hashmap在put和get时候的原理,这个问题也就比较简单了。先说put,hashmap会对key值做hashcode的操作,这个算出来的值也就是为了找到value存放的位置,如果这个位置上已经有元素,则以链表的方式添加在链表的尾部,如果没有元素,则放在该位置上。再说get,hashmap会根据get函数中传入的key,用hashcode找到元素的位置,如果该位置有一个元素则直接返回,如果多个元素,根据equals方法判断该位置链表中的元素是否为我要找的元素。那么回到本问题,对于标红出现的原因,第一次put的时候,根据"1"作为key算出来的hash值和第二次put"1"的时候算出来的hash值是不一样的
这个问题涉及HashMap存储key-value的原理。其实看源码,一切了然。我曾经看过源码,HashMap内部其实维护着一个链表数组。数组中的每个元素(即每条链表)维护着key-value链。当一对key-value被put进来的时候,先得到key的hashcode,然后以某种算法通过hashcode得到这对key-value应该被放到数组的哪个位置上。也就是说key-value放到数组的哪个位置取决于hashcode。而题主,虽然前后两次put进的key是同一个Person,但是age变了,hashcode也就变了。因此被放到了数组的不同位置上,最终导致key重复。