按照下面的代码,hashmap的初始dafault容量为16,LF为0.75,因此当我输入第13个元素时,应进行重新哈希处理,并且由于我提供了一个恒定的哈希码,因此它在内部维护一个链表以维护插入顺序。因此,直到第10个元素都能按预期工作,但是当我输入第11个元素时,它会改变顺序。 谁能帮助我了解为什么它仅在第11个元素插入时发生。
class A{
int a;
A(int a){
this.a = a;
}
@Override
public int hashCode() {
return 7;
}
@Override
public String toString() {
return "" + a + "";
}
}
class Base {
public static void main(String[] args) {
Map<Object, Integer> m = new HashMap<Object, Integer>();
m.put(new A(1), 1);
m.put(new A(2), 1);
m.put(new A(3), 1);
m.put(new A(4), 1);
m.put(new A(5), 1);
m.put(new A(6), 1);
m.put(new A(7), 1);
m.put(new A(8), 1);
m.put(new A(9), 1);
m.put(new A(10), 1);
//m.put(new A(11), 1);
System.out.println(m);
}
}
我将输出到第十个元素的输出:
{1=1, 2=1, 3=1, 4=1, 5=1, 6=1, 7=1, 8=1, 9=1, 10=1}
输入第11个元素后得到的输出:
{4=1, 1=1, 2=1, 3=1, 5=1, 6=1, 7=1, 8=1, 9=1, 10=1, 11=1}
它应该保持插入顺序,还是内部使用RB树,那么在这种情况下,它在此处使用哪个遍历?
答案 0 :(得分:22)
它应该保持插入顺序,还是内部使用RB树,那么在这种情况下,它在此处使用哪个遍历?
没有“应该”; HashMap
不保证任何顺序。当前实现中实际发生的情况取决于两个常量TREEIFY_THRESHOLD = 8
和MIN_TREEIFY_CAPACITY = 64
。
当一个存储桶中的项目数超过前一个存储桶时,该存储桶将被转换为节点树,除非地图的总容量小于后者的常数,在这种情况下,存储容量将增加一倍。
>因此,当您插入第9个对象时,容量将从16增加到32,插入第10个对象将导致从32增加到64,然后,插入第11个元素将导致将存储桶转换为树。 / p>
无论是否有实际好处,这种转换将永远发生。由于对象具有相同的哈希码且未实现Comparable
,因此最终将使用其标识哈希码来确定顺序。这可能会导致顺序更改(在我的环境中,不会)。
答案 1 :(得分:6)
它与指定的哈希码编号(即7)无关,而与您的hascode保持不变的原因无关。原因如下:
我遍历了HashMap的put方法的源代码,并且有一个常量TREEIFY_THRESHOLD
决定何时将常规存储桶转换为树。
static final int TREEIFY_THRESHOLD = 8;
put方法的代码段如下(Put方法调用putVal方法):
.
.
.
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
.
.
.
记下包含if (binCount >= TREEIFY_THRESHOLD - 1)
条件的行。一旦发现存储桶已满TREEIFY_THRESHOLD
个容量,就会调用treeifyBin()
方法。
仅当满足resize()
时,此方法才调用MIN_TREEIFY_CAPACITY
方法。
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
do {
TreeNode<K,V> p = replacementTreeNode(e, null);
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
在上面的代码段中查找以下情况
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
然后,调整大小方法会根据其具有的多个条件检查相应地增加地图的大小。基本上可以通过负载系数来增加容量。
就像没有树状树一样。树中元素的数量减少。取消树化操作以UNTREEIFY_THRESHOLD
(即6为基础)执行。
我引用了this链接来浏览Hashmap代码。