在 Java 8 中使用 Java 7 HashMap
Using Java 7 HashMap in Java 8
我已将 Java 应用程序更新为 Java 8. 该应用程序严重依赖 HashMap。
当我运行基准测试时,我看到了不可预知的行为。对于某些输入,应用程序运行得比以前更快,但对于更大的输入,它会一直变慢。
我检查了分析器,最耗时的操作是 HashMap.get。我怀疑这些变化
是因为Java8中修改了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<Node, Node> 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. 有什么想法吗?
你的hashCode()
很差。在您发布的示例中,您有 250000 个唯一值,但只有 15969 个唯一哈希码。因为很多碰撞,Java 8 swaps lists with trees。在您的情况下,它只会增加开销,因为许多元素不仅在哈希 table 中具有相同的位置,而且具有相同的哈希码。不管怎样,这棵树最终还是一个链表。
有几种方法可以解决此问题:
改进你的 hashCode。 return k1 * 500 + k2;
解决了这个问题。
使用THashMap。开放式寻址在发生冲突时效果更好。
使 Node
实现 Comparable
。 HashMap
将在发生冲突时使用它来构建平衡树。
我已将 Java 应用程序更新为 Java 8. 该应用程序严重依赖 HashMap。 当我运行基准测试时,我看到了不可预知的行为。对于某些输入,应用程序运行得比以前更快,但对于更大的输入,它会一直变慢。
我检查了分析器,最耗时的操作是 HashMap.get。我怀疑这些变化 是因为Java8中修改了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<Node, Node> 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. 有什么想法吗?
你的hashCode()
很差。在您发布的示例中,您有 250000 个唯一值,但只有 15969 个唯一哈希码。因为很多碰撞,Java 8 swaps lists with trees。在您的情况下,它只会增加开销,因为许多元素不仅在哈希 table 中具有相同的位置,而且具有相同的哈希码。不管怎样,这棵树最终还是一个链表。
有几种方法可以解决此问题:
改进你的 hashCode。
return k1 * 500 + k2;
解决了这个问题。使用THashMap。开放式寻址在发生冲突时效果更好。
使
Node
实现Comparable
。HashMap
将在发生冲突时使用它来构建平衡树。