在Java 8中使用Java 7 HashMap

我已将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更好。任何想法?

你的hashCode()很差。 在您发布的示例中,您发布了250000个唯一值,但只有15969个唯一哈希码。 由于大量冲突, Java 8使用树交换列表 。 在您的情况下,它只会增加开销,因为许多元素不仅在哈希表中具有相同的位置,而且具有相同的哈希码。 无论如何,树最终都是链表。

有几种方法可以解决这个问题:

  1. 改进你的hashCode。 return k1 * 500 + k2; 解决了这个问题。

  2. 使用THashMap 。 在发生碰撞时,开放式寻址应该更好。

  3. 使Node实现Comparable 。 这将由HashMap用于在发生冲突时构建平衡树。