作者:孤l叶_991 | 来源:互联网 | 2023-05-28 15:22
我已经将Java应用程序更新为Java 8.应用程序严重依赖于HashMaps.当我运行基准时,我看到了不可预测的行为.对于某些输入,应用程序比以前运行得更快,但对于较大的输入,它会一直较慢.
我检查过探查器,最耗时的操作是HashMap.get.我怀疑这些更改是由Java 8中的HashMap修改引起的,但可能不是这样,因为我已经更改了其他一些部分.
有没有一种简单的方法可以将原始的Java 7 HashMap挂钩到我的Java 8应用程序中,这样我只需要更改hashmap实现,看看我是否仍然观察到性能的变化.
以下是一个尝试模拟我的应用程序正在执行的操作的最小程序.基本思想是我需要在应用程序中共享节点.在某个运行时点,如果某个节点已根据某些整数属性而不存在,则应该检索或创建该节点.以下仅使用两个整数,但在实际应用程序中我有一个,两个和三个整数键.
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class Test1 {
static int max_k1 = 500;
static int max_k2 = 500;
static Map map;
static Random random = new Random();
public static void main(String[] args) {
for (int i = 0; i <15; i++) {
long start = System.nanoTime();
run();
long end = System.nanoTime();
System.out.println((end - start) / 1000_000);
}
}
private static void run() {
map = new HashMap<>();
for (int i = 0; i <10_000_000; i++) {
Node key = new Node(random.nextInt(max_k1), random.nextInt(max_k2));
Node val = getOrElseUpdate(key);
}
}
private static Node getOrElseUpdate(Node key) {
Node val;
if ((val = map.get(key)) == null) {
val = key;
map.put(key, val);
}
return val;
}
private static class Node {
private int k1;
private int k2;
public Node(int k1, int k2) {
this.k1 = k1;
this.k2 = k2;
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + k1;
result = 31 * result + k2;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Node))
return false;
Node other = (Node) obj;
return k1 == other.k1 && k2 == other.k2;
}
}
}
基准测试是原始的,但仍然是Java 8上15次运行的结果:
8143
7919
7984
7973
7948
7984
7931
7992
8038
7975
7924
7995
6903
7758
7627
这适用于Java 7:
7247
6955
6510
6514
6577
6489
6510
6570
6497
6482
6540
6462
6514
4603
6270
基准测试是原始的,所以如果熟悉JMH或其他基准测试工具的人运行它,我很感激,但从我观察到的结果来看,Java 7更好.任何想法?
1> Piotr Praszm..:
你hashCode()
很穷.在您发布的示例中,您发布了250000个唯一值,但只有15969个唯一哈希码.由于存在大量冲突,Java 8使用树交换列表.在您的情况下,它只会增加开销,因为许多元素不仅在哈希表中具有相同的位置,而且具有相同的哈希码.无论如何,树最终都是一个链表.
有几种方法可以解决这个问题:
改进你的hashCode.return k1 * 500 + k2;
解决了这个问题.
使用THashMap.在发生碰撞时,开放式寻址应该更好.
制作Node
工具Comparable
.这将用于HashMap
在发生冲突时构造平衡树.
Java 8看起来较慢,因为当哈希函数和你的一样糟糕时,它用于在哈希冲突的情况下尝试提高性能的技术会受到更大的伤害.