OCP书上的这句话是正确的吗?

HashSet将其元素存储在哈希表中,这意味着keys are a hash和值是一个对象.

我已经创建了以下代码片段:

class A
{
    private String s;
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException  {
        HashSet<A> elems =  new HashSet<>(Set.of(new A("abc"), new A("cba"), new A("a")));
        Field field = elems.getClass().getDeclaredField("map");
        field.setAccessible(true);
        HashMap privateMap = (HashMap) field.get(elems);
    }

    public A(String s)
    {
        this.s = s;
    }
}

And as a result I retrieved the following HashMap:
enter image description here

它看起来像是对象本身是HashMap中的键,而不是它的散列是键.这本书有没有错,或者我看到的是不相关的东西?

推荐答案

当然,它是对象,而不是散列.一些个别显而易见或至少容易核实的事实:

  • 给定散列的大小是固定的(存在2^32个不同的散列,仅此而已),并且存在超过2^32个不同值的对象(它们的数量是无限的),那么它是a mathematical certainty that different strings exist that hash to the same value.如果你喜欢看一些视频或诸如此类的东西来更详细地解释这个概念,这被称为鸽子洞原理.
  • 如果HashMap/HashSet认为different strings仍然等于just,那将是非常恼人的,因为它们的哈希码恰好冲突.事实上,这将意味着这些类型不符合它们自己的规范(Javadoc),因为它们清楚地表明冲突会减慢速度,但在其他方面不会成为问题.
  • 因此,HashSet/Map需要原始对象,以便它可以使用.equals()来验证事物是否为actually equal.

Hashmap/set的工作方式如下:

  • 使用对象的hashCode查找要查看的"存储桶".
  • 然后只需扫描,一次一个,桶中的每个物体.是的,这是很慢的-因此您不会有want吨的散列冲突.

因此,这就是结论:

  • 假设两个对象abifa.equals(b)为真,那么a.hashCode() == b.hashCode()就必须为真,否则您不能在哈希集/哈希图中使用这些对象,因为如果您这样做了,就会发生奇怪的事情,您刚刚添加的键/对象会消失得无影无踪,但在遍历时仍然会出现,依此类推.
  • 反之亦然--如果a.hashCode() == b.hashCode()是真的,那么a.equals(b)就不一定是真的.这可能只是一次碰撞,这很好.
  • 这意味着理论上@Override public int hashCode() { return 1; }是有效的.的确如此;只要equals方法运行良好,HashMap和HashSet就会按照描述进行操作.然而,这样的 map /集将是O(n)个-它们的性能与其中的项目数量呈线性关系.这是意想不到的;hashset/map的要点是它们的性能不会随着它们的增长而有意义地改变.HashMap/Set不是魔术--它们需要良好的hashCode() Imp才能实现其性能promise .

如果你读到一些文档或书籍说哈希码是关键,那么这要么来自过于简单化的部分(重音在over上-这可能不是在教程或解释中放入的好东西),要么是作者误解了这种东西的工作原理,或者它是用另一种系统/语言对哈希图实现的描述,该系统/语言采用了非常可疑的捷径,将"相等的哈希"等同于".."这样就相等了.考虑到鸽子原则和所有这些,这不是一个好主意.如果你必须这样做,使用一个好的散列算法,并且比32位多得多!

Java相关问答推荐

我想了解Java中的模块化.编译我的应用程序时,我有一个ResolutionException

在AnyLogic中增加变量计数

在Java Stream上调用collect方法出现意外结果

XPages-在第二次点击按钮之前延迟

Java中的死锁及其重入锁和锁

如何从Keyloak映射Hibernate实体中的用户

如何在antlr4中跳过所有反斜杠-换行符而保留换行符?

如何从日志(log)行中删除包名称?

带有Health Check的Spring Boot BuildPack打破了本机映像构建过程

使用htmlunit和java单击按钮

如何在JUNIT测试中覆盖ExecutorService?

垃圾收集时间长,会丢弃网络连接,但不会在Kubernetes中反弹Pod

当b是一个字节并且在Java中值为-1时,为什么b>;>;>;1总是等于-1?

无法将GSON导入到我的JavaFX Maven项目

在Oracle db中,当我们提供字符串而不是数字时,比较是如何工作的?

具有多个模式的DateTimeForMatter的LocalDate.parse失败

使用MediaPlayer类在一段时间后停止播放音乐

为什么创建Java动态代理需要接口参数

rest api服务 spring 启动中出现IllegalFormatConversionException

双对象供应商