在 Java 8 中使用 Java 7 HashMap

2022-09-03 01:27:24

我已将 Java 应用程序更新到 Java 8。该应用程序严重依赖HashMaps。当我运行基准测试时,我看到了不可预测的行为。对于某些输入,应用程序的运行速度比以前快,但对于较大的输入,它始终较慢。

我已经检查了探查器,最耗时的操作是HashMap.get。我怀疑这些变化是由于Java 8中的HashMap修改造成的,但事实可能并非如此,因为我已经更改了其他一些部分。

有没有一种简单的方法,我将原始的Java 7 HashMap挂接到我的Java 8应用程序中,这样我只能更改哈希映射实现,看看我是否仍然观察到性能的变化。

下面是一个最小程序,它尝试模拟我的应用程序正在执行的操作。基本思想是我需要共享应用程序中的节点。在某些运行时,如果节点基于某些整数属性尚不存在,则应检索或创建该节点。下面只使用两个整数,但在实际应用程序中,我有一个,两个和三个整数键。

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的结果更好。有什么想法吗?


答案 1

你很穷。例如,您发布了250000个唯一值,但只有15969个唯一哈希代码。由于冲突很多,Java 8 将列表与树交换。在你的例子中,它只会增加开销,因为许多元素不仅在哈希表中具有相同的位置,而且具有相同的哈希代码。无论如何,树最终都会成为一个链接列表。hashCode()

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

  1. 改进您的哈希代码。 解决了问题。return k1 * 500 + k2;

  2. 使用 THashMap。开放寻址应该在发生冲突时更好地工作。

  3. 使实现 .这将用于在发生冲突时构造平衡树。NodeComparableHashMap


答案 2

推荐