我有一个由7.6百万行组成的文件。每一行都是形式为: A,B,C,D的格式,其中B,C,D是用于计算A的重要程度的值,而A则是一个字符串标识符,对于每一行都是唯一的。我的方法:
private void read(String filename) throws Throwable {
BufferedReader br = new BufferedReader(new FileReader(filename));
Map<String, Double> mmap = new HashMap<>(10000000,0.8f);
String line;
long t0 = System.currentTimeMillis();
while ((line = br.readLine()) != null) {
split(line);
mmap.put(splitted[0], 0.0);
}
long t1 = System.currentTimeMillis();
br.close();
System.out.println("Completed in " + (t1 - t0)/1000.0 + " seconds");
}
private void split(String line) {
int idxComma, idxToken = 0, fromIndex = 0;
while ((idxComma = line.indexOf(delimiter, fromIndex)) != -1) {
splitted[idxToken++] = line.substring(fromIndex, idxComma);
fromIndex = idxComma + 1;
}
splitted[idxToken] = line.substring(fromIndex);
}
在 "profiling" 的目的下,将虚拟值 0.0 插入,并为该类定义了一个简单的字符串数组splitted。我最初用的是String类的split()方法,但发现上述方法更快。
当我运行以上代码时,解析文件需要12秒的时间,这比我想象中的要慢得多。例如,如果我用字符串向量替换HashMap,并只取每行的第一个条目(即不为其设置关联值,因为这应该是平摊常数),则整个文件可以在不到3秒的时间内读取。
这表明对于这个 HashMap ,可能有很多冲突(我尽量通过预分配大小并相应地设置负载因子来最小化重调整次数),或者 hashCode() 函数某种程度上很慢。 我怀疑是 (ii) 因为如果我使用一个 HashSet ,那么可以在不到4秒钟的时间内读取文件。
我的问题是:为什么HashMap的性能如此之慢?hashCode() 对于这么大的映射不足够吗,还是我忽略了某些根本性的东西?
0.0
虚拟值。0.0
被Double.valueOf
替换,每次都会创建一个新对象。在HashSet
中只使用一个预分配的虚拟对象。我不确定这是原因,但可能是这样。 - esin88splitted[]
的最后一个元素总是包含整个行,这不是你想要的。 - user207421HashSet
内部由HashMap
支持,因此唯一的区别是您的虚拟0.0
的自动装箱。 - bashnesnosString.split()
较慢,因为它在每次调用时都会分配一个新的正则表达式Pattern
。尝试创建一个private static final Pattern SPLITTER = Pattern.compile(",");
然后使用SPLITTER.split(line)
。 - AngerClown