Java 为什么HashMap对不可比较的键使用TreeNode?

Java 为什么HashMap对不可比较的键使用TreeNode?,java,java-8,hashmap,Java,Java 8,Hashmap,我知道,在Java8中,HashMap针对分布不均匀的hashCode进行了优化。当超过阈值时,它会将bucket中的节点从链表重建到树中。另外,这种优化对于不可比较的密钥不起作用,至少性能没有提高。在下面的示例中,我将不可比较的键放入HashMap 但是检查堆转储显示节点被重新排列到树中 我的问题是,如果不能提高性能,为什么要将节点重建到树中?在这种情况下,根据哪些标准比较节点,以确定哪个键应该是右节点,哪个键应该是左节点?我认为您误解了答案的意思。不需要Compariable,它只是一种优化

我知道,在Java8中,HashMap针对分布不均匀的hashCode进行了优化。当超过阈值时,它会将bucket中的节点从链表重建到树中。另外,这种优化对于不可比较的密钥不起作用,至少性能没有提高。在下面的示例中,我将不可比较的键放入HashMap

但是检查堆转储显示节点被重新排列到树中


我的问题是,如果不能提高性能,为什么要将节点重建到树中?在这种情况下,根据哪些标准比较节点,以确定哪个键应该是右节点,哪个键应该是左节点?

我认为您误解了答案的意思。不需要Compariable,它只是一种优化,可以在散列相等时使用,以决定将条目移动到左侧或右侧完全平衡的红黑树节点。稍后,如果键不可比较,它将使用System.identityHashcode

找出哪个键应该是右节点,哪个键应该是左节点


它向右移动-较大的键向右移动,但是树可能需要平衡。通常,您可以查找一棵树如何成为完全平衡的红黑树的确切算法,如

Afaik它不使用标识哈希代码,只使用key对象的哈希代码。@jornverne它使用。请参阅内部的TieborkOrder方法HashMap@Eugene这是散列码相等时的回退,这不一定是散列冲突的情况,尽管我猜在本例中是这样。如果你看一下它在putTreeVal中的使用位置,你会看到它在这之前尝试使用密钥的哈希代码。很抱歉,我误解了你的意思@尤金是对的。我忘了订领带了。它首先通过自己的散列计算目录,然后使用可比较的,最后使用System.identityHashCode,这是一个全局唯一的散列代码。@ArtemPetrov这是一个很好的观点!以下是对此的解释:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

class Main {
    public static void main(String[] args) throws InterruptedException {
        Map<Key, Integer> map = new HashMap<>();

        IntStream.range(0, 15)
                .forEach(i -> map.put(new Key(i), i));

        // hangs the application to take a Heap Dump
        TimeUnit.DAYS.sleep(1);
    }
}

final class Key {
    private final int i;

    public Key(int i) {
        this.i = i;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Key key = (Key) o;
        return i == key.i;
    }

    @Override
    public int hashCode() {
        return 1;
    }
}