Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/318.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在HashMap中使用字符串键不是个好主意吗?_Java_String_Map_Hashcode - Fatal编程技术网

Java 在HashMap中使用字符串键不是个好主意吗?

Java 在HashMap中使用字符串键不是个好主意吗?,java,string,map,hashcode,Java,String,Map,Hashcode,我知道String类的方法不能保证为不同的String-s生成唯一的哈希代码。我看到了将字符串键放入HashMap-s(使用默认的String hashCode()方法)的许多用法。如果一个mapput替换了一个HashMap条目,而该条目以前是用一个真正不同的字符串键放在map上的,那么很多这种用法可能会导致严重的应用程序问题 遇到String.hashCode()为不同的String-s返回相同值的情况的几率有多大?当密钥是字符串时,开发人员如何解决这个问题?我强烈怀疑该方法不能通过查看来确

我知道String类的方法不能保证为不同的String-s生成唯一的哈希代码。我看到了将字符串键放入HashMap-s(使用默认的String hashCode()方法)的许多用法。如果一个map
put
替换了一个HashMap条目,而该条目以前是用一个真正不同的字符串键放在map上的,那么很多这种用法可能会导致严重的应用程序问题


遇到String.hashCode()为不同的String-s返回相同值的情况的几率有多大?当密钥是字符串时,开发人员如何解决这个问题?

我强烈怀疑该方法不能通过查看来确定密钥是否相同

肯定会有a的可能性,因此,如果确实存在两个
字符串
s具有从
hashCode
返回的相同值的情况,则可以期望调用该方法以确保s真正相等

因此,仅当且仅当返回的值相等,且方法返回的值为
true
时,新键
String
才会被判断为与
HashMap
中已有的键相同的键
String

还要补充的是,这种思想对于
String
以外的类也是正确的,因为类本身已经有了和方法

编辑


因此,要回答这个问题,不,使用
字符串作为
HashMap

的键不是一个坏主意。您正在谈论的是哈希冲突。无论哈希代码的类型是什么,哈希冲突都是一个问题。所有使用hashCode(例如HashMap)的类都可以很好地处理哈希冲突。例如,HashMap可以在每个bucket中存储多个对象

除非您自己调用hashCode,否则不要担心它。哈希冲突虽然很少见,但不会破坏任何东西。

开发人员不必在HashMap中解决哈希冲突问题,以实现程序的正确性

这里有两件关键的事情需要理解:

  • 冲突是散列的一个固有特性,必须加以解决。可能值的数量(在您的例子中是字符串,但它也适用于其他类型)远远大于整数的范围。
  • 哈希的每一种用法都有一种处理冲突的方法,Java集合(包括HashMap)也不例外。
  • 在平等性测试中不涉及哈希。相等的对象必须具有相等的哈希代码,这是事实,但反之则不然:许多值将具有相同的哈希代码。因此,不要尝试使用哈希代码比较来代替相等。收藏不会。它们使用散列来选择子集合(在Java集合世界中称为bucket),但它们使用.equals()来实际检查相等性。
  • 您不仅不必担心冲突会在集合中导致不正确的结果,而且对于大多数应用程序,您也*通常*不必担心性能—Java哈希集合在管理哈希代码方面做得相当好。
  • 更好的是,对于您询问的情况(字符串作为键),您甚至不必担心哈希代码本身,因为Java的String类生成了非常好的哈希代码。提供的大多数Java类也是如此。 如果您需要,请提供更多详细信息:

    散列的工作方式(特别是在像Java的HashMap这样的散列集合的情况下,这就是您所问的)是:

    • HashMap将您给它的值存储在一个称为bucket的子集合集合中。这些实际上是作为链表实现的。其中有一个有限的数量:iirc,默认情况下为16,并且随着您将更多项目放入地图中,数量会增加。存储桶应该总是多于值。举一个例子,使用默认值,如果向HashMap添加100个条目,将有256个bucket

    • 可以用作映射中键的每个值都必须能够生成一个整数值,称为hashcode

    • HashMap使用此hashcode选择一个bucket。最终,这意味着取整数值
      桶数,但在此之前,Java的HashMap有一个内部方法(称为
      hash()
      ),它调整hashcode以减少一些已知的聚集源

    • 在查找值时,HashMap选择bucket,然后使用
      .equals()
      通过链表的线性搜索来搜索单个元素

    因此:您不必为了正确性而绕过冲突,通常也不必为性能而担心冲突,如果您使用的是本机Java类(如String),也不必担心生成哈希代码值

    在您必须编写自己的hashcode方法的情况下(这意味着您编写了一个具有复合值的类,如名字/姓氏对),事情会变得稍微复杂一些。这里很有可能出错,但这不是火箭科学。首先,要知道:为了确保正确性,您必须做的唯一一件事就是确保相等的对象产生相等的哈希代码。因此,如果为类编写hashcode()方法,还必须编写equals()方法,并且必须检查每个方法中的相同值

    可以编写一个hashcode()方法,该方法不好但正确,我的意思是,它将满足“相等对象必须产生相等的hashcodes”约束,但由于存在大量冲突,其性能仍然很差

    规范退化的最坏情况是编写一个方法,该方法在所有情况下仅返回一个常量值(例如,3)。这意味着每个值都将散列到同一个bucket中

    它仍然可以工作,但性能会降低
    
    public class SimpleName {
        private String firstName;
        private String lastName;
        public SimpleName(String firstName, String lastName) {
            super();
            this.firstName = firstName;
            this.lastName = lastName;
        }
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result
                    + ((firstName == null) ? 0 : firstName.hashCode());
            result = prime * result
                    + ((lastName == null) ? 0 : lastName.hashCode());
            return result;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            SimpleName other = (SimpleName) obj;
            if (firstName == null) {
                if (other.firstName != null)
                    return false;
            } else if (!firstName.equals(other.firstName))
                return false;
            if (lastName == null) {
                if (other.lastName != null)
                    return false;
            } else if (!lastName.equals(other.lastName))
                return false;
            return true;
        }
    }
    
    
    Hash           Lowercase      Random UUID  Numbers 
    =============  =============  ===========  ==============
    Murmur            145 ns      259 ns          92 ns
                        6 collis    5 collis       0 collis
    FNV-1a            152 ns      504 ns          86 ns
                        4 collis    4 collis       0 collis
    FNV-1             184 ns      730 ns          92 ns
                        1 collis    5 collis       0 collis*
    DBJ2a             158 ns      443 ns          91 ns
                        5 collis    6 collis       0 collis***
    DJB2              156 ns      437 ns          93 ns
                        7 collis    6 collis       0 collis***
    SDBM              148 ns      484 ns          90 ns
                        4 collis    6 collis       0 collis**
    CRC32             250 ns      946 ns         130 ns
                        2 collis    0 collis       0 collis
    
    Avg Time per key    0.8ps       2.5ps         0.44ps
    Collisions (%)      0.002%      0.002%         0%