【发布时间】:2017-03-01 11:05:12
【问题描述】:
我有一个包含 7.6M 行的文件。每行的格式为: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);
}
其中插入虚拟值 0.0 以用于“分析”目的,并拆分为为该类定义的简单字符串数组。我最初使用 String 的 split() 方法,但发现上面的方法更快。
当我运行上面的代码时,解析文件需要 12 秒,这比我认为的要多得多。例如,如果我将 HashMap 替换为字符串向量并仅从每一行中获取第一个条目(即,我没有将关联值与其关联,因为这应该是摊销常量),则整个文件可以在不到3 秒。
这向我表明 (i) HashMap 中有很多冲突(我试图通过预先分配大小并相应地设置负载因子来最小化调整大小的次数)或 (ii) hashCode() 函数有点慢。我怀疑它的 (ii),因为如果我使用 HashSet,可以在 4 秒内读取文件。
我的问题是:HashMap 执行如此缓慢的原因可能是什么? hashCode() 对于这种大小的地图是不够的,还是我根本上忽略了一些东西?
【问题讨论】:
-
尝试用一些静态最终常量替换您的
0.0虚拟值。0.0被Double.valueOf替换,每次都会创建一个新对象。而在HashSet中,只使用了一个预先分配的虚拟对象。我不确定是不是这个原因,但可能是 -
splitted[]的最后一个元素将始终占据整行。这不是你想要的。 -
HashSet在内部由HashMap支持,所以唯一的区别是你的虚拟0.0的自动装箱。 -
您能否将 io 和标记化与地图插入分开,以确保您没有专注于错误的事情? micro benchmarking
-
String.split()速度较慢,因为它会在每次调用时分配一个新的正则表达式Pattern。尝试创建private static final Pattern SPLITTER = Pattern.compile(",");然后SPLITTER.split(line)。
标签: java performance hashmap hashcode