Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/15.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中是不可变的?_Java_Hashtable - Fatal编程技术网

为什么键在Java中是不可变的?

为什么键在Java中是不可变的?,java,hashtable,Java,Hashtable,我为这个相当幼稚的问题道歉,但我相信我自己的回答是幼稚的。我认为键(在哈希表中)是不可变的,因为我们不希望以某种方式意外地改变键,从而扰乱哈希表的排序。这是正确的解释吗?如果是这样,它怎么可能更正确呢?在哈希表期间。put对键进行哈希运算,并根据哈希值将其值存储在多个存储桶(即键值对列表)中的一个,例如: bucket[key.hashcode() % numberOfBuckets].add(key, value) 如果密钥的hashcode在插入后发生更改,则它可能位于错误的存储桶中,您将

我为这个相当幼稚的问题道歉,但我相信我自己的回答是幼稚的。我认为键(在哈希表中)是不可变的,因为我们不希望以某种方式意外地改变键,从而扰乱哈希表的排序。这是正确的解释吗?如果是这样,它怎么可能更正确呢?

哈希表期间。put
对键进行哈希运算,并根据哈希值将其值存储在多个存储桶(即键值对列表)中的一个,例如:

bucket[key.hashcode() % numberOfBuckets].add(key, value)
如果密钥的
hashcode
在插入后发生更改,则它可能位于错误的存储桶中,您将无法找到它,并且哈希表将在该密钥的任何
get
上错误地返回
null

旁白:了解哈希表的内部工作原理有助于您了解高质量的
hashcode
函数对键的重要性。因为一个糟糕的hashcode函数可能会导致存储桶中的密钥分布不好。由于bucket只是列表,因此会导致大量线性搜索,这大大降低了哈希表的有效性。e、 这个糟糕的hashcode函数将所有内容放在一个bucket中,因此实际上它只是一个列表

public int hashcode { return 42; /*terrible hashcode example, don't use!*/ }
这也是质数出现在好的hashcode函数中的一个原因,例如:

public int hashcode {
    int hash = field1.hashcode();
    hash = hash*31 + field2.hashcode(); //note the prime 31
    hash = hash*31 + field3.hashcode();
    return hash;
}

总的想法是正确的,但它的细节是错误的

哈希表中的键不必是不可变的,它是调用它们的
hashCode()
(和
等于
)方法的结果,该方法需要保持不可变和一致性(也就是说,哈希表的行为是可预测的)

从高层次的角度来看,这是因为哈希表的工作方式:当插入(
)对时,
)的哈希代码在内部用于计算一个“bucket”,将值放入其中。当通过
检索
时,再次计算
哈希代码
,以找到返回的存储桶

现在,如果在插入和检索之间的任何时间点,调用
hashCode
的结果发生变化,“查找桶”将不同于“插入”桶,事情的行为将无法预测

总之,给定一个如下所示的键对象(两个内部字符串组成对象,但只有一个,
partOfHashCode
在hashCode/equals中考虑):

这样使用也可以:

public static void main(String[] args) {

Map<Key, String> myMap = new HashMap<>();
Key key = new Key();
key.partOfHashCode = "myHash";

myMap.put(key, "value");

key.notPartOfHashCode = "mutation of the key, but not of its hash/equals definition";

System.out.println(myMap.get(key));
}
publicstaticvoidmain(字符串[]args){
Map myMap=newhashmap();
键=新键();
key.partOfHashCode=“myHash”;
myMap.put(键,“值”);
key.notPartOfHashCode=“密钥的变异,但不是其哈希/等于定义的变异”;
System.out.println(myMap.get(key));
}
(这会在控制台中记录“value”对象)

但这样使用是不好的

public static void main(String[] args) {

  Map<Key, String> myMap = new HashMap<>();
  Key key = new Key();
  key.partOfHashCode = "myHash";

  myMap.put(key, "value");

  key.partOfHashCode = "mutation of the hashCode of the key";

  System.out.println(myMap.get(key));
}
publicstaticvoidmain(字符串[]args){
Map myMap=newhashmap();
键=新键();
key.partOfHashCode=“myHash”;
myMap.put(键,“值”);
key.partOfHashCode=“密钥hashCode的变异”;
System.out.println(myMap.get(key));
}
(最后一个示例可能在控制台中记录“null”)


有关此主题的更多信息,还应阅读hashCode/equals一致性。

Java中没有固有的保证,即HashTable-键是不可变的。甚至不能保证它们的
hashcode
保持不变。但是,如果您添加的键具有可变的
hashCode
,您就有麻烦了。假设插入的密钥的
hashCode
值为1。然后将其插入到对应于1的散列桶中。然后将对象更改为
hashCode
为2,并调用
hashMap.get(key)
。当对象仍在
哈希表中时
系统将在对应于2的bucket中查找,但在那里找不到它。您甚至无法
删除该条目,因为找不到它

tl;dr要使应用程序正常工作,键需要具有不可变的
哈希代码,但您必须自己处理这一事实

public static void main(String[] args) {

  Map<Key, String> myMap = new HashMap<>();
  Key key = new Key();
  key.partOfHashCode = "myHash";

  myMap.put(key, "value");

  key.partOfHashCode = "mutation of the hashCode of the key";

  System.out.println(myMap.get(key));
}