繁体   English   中英

HashMap在Java中的奇怪行为

[英]Strange behavior of HashMap in Java

我在下面的类中找到了hashmap的一些奇怪行为。

class Employee {

  private String a;
  private int b;

  public Employee(String a, int b) {
    this.a = a;
    this.b = b;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((a == null) ? 0 : a.hashCode());
    result = prime * result + b;
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Employee other = (Employee) obj;
    if (a == null) {
        if (other.a != null)
            return false;
    } else if (!a.equals(other.a))
        return false;
    if (b != other.b)
        return false;

    return true;
  }

  public static void main(String[] args) {
    HashMap<Employee,Integer> map = new HashMap<>();

    for(int i = 0; i < 13; i++) {
        map.put(new Employee( i + "", i), i + i);
    }
  }
}

当我使用新的Employee(“”,i)作为在地图中存储数据的关键时,它正常工作并在第12个节点插入后调整地图大小。 但是在使用新的Employee(i +“”,i)作为键时,它显示出奇怪的行为,在使用此键添加第10个元素时,它将地图从16调整为32,并且在添加第11个元素时,它再次将地图从32调整为如果你发现任何这种行为的原因,请帮忙。

究其原因-在Java中的HashMap 8.新的组织在特定的箱子里面列表变得太长HashMap迁移该列表的树,而不是一个链表-这个过程被称为treeifying。

TREEIFY_THRESHOLD = 8表示当在给定的bin中有8个条目时,则给定的bin应该在二叉树中存储冲突值而不是链表,从而将该bin的搜索复杂度从O(n)改变O(log n)

if (binCount >= TREEIFY_THRESHOLD - 1)
                treeifyBin(tab, hash);

方法treeifyBin替换bin中的所有链接节点以获取散列,除非表太小,在这种情况下它会调整表的大小;

因此,在您的情况下,您获得64大小(此代码调整大小两次,将选项卡大小增加到32和64(MIN_TREEIFY_CAPACITY)):

if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();

正如@G_H所提到的,我相信你指的是地图的内部结构,通过调试器可见。 HashMap使用hashCode()方法对“桶”中的对象进行分组。

您重写的hashCode()方法使用String成员a的值。 当a为“”时,其哈希码为0,因此您插入的元素的哈希码相对于彼此相对更近,而不是设置为具有更高哈希码的更有意义的字符串。

由于某种原因(取决于如何准确实现散列桶),当散列码值进一步分开时,HashMap对象决定更快地扩大其内部结构。

对于您插入的元素,请查看其哈希码值,这将是有意义的。

我尝试重写代码的has方法。 使用您的实现(Used Reflection来获取地图详细信息),

Loop 0 : Capacity : 16, Factor : 12, Current Size : 1
Loop 1 : Capacity : 16, Factor : 12, Current Size : 2
Loop 2 : Capacity : 16, Factor : 12, Current Size : 3
Loop 3 : Capacity : 16, Factor : 12, Current Size : 4
Loop 4 : Capacity : 16, Factor : 12, Current Size : 5
Loop 5 : Capacity : 16, Factor : 12, Current Size : 6
Loop 6 : Capacity : 16, Factor : 12, Current Size : 7
Loop 7 : Capacity : 16, Factor : 12, Current Size : 8
Loop 8 : Capacity : 32, Factor : 24, Current Size : 9
Loop 9 : Capacity : 64, Factor : 48, Current Size : 10
Loop 10 : Capacity : 64, Factor : 48, Current Size : 11
Loop 11 : Capacity : 64, Factor : 48, Current Size : 12
Loop 12 : Capacity : 64, Factor : 48, Current Size : 13

重写如下,

public int hashCode() {
    final int prime = 31;
    return prime * this.b;
}

然后尺寸增加是预期的,

Loop 0 : Capacity : 16, Factor : 12, Current Size : 1
Loop 1 : Capacity : 16, Factor : 12, Current Size : 2
Loop 2 : Capacity : 16, Factor : 12, Current Size : 3
Loop 3 : Capacity : 16, Factor : 12, Current Size : 4
Loop 4 : Capacity : 16, Factor : 12, Current Size : 5
Loop 5 : Capacity : 16, Factor : 12, Current Size : 6
Loop 6 : Capacity : 16, Factor : 12, Current Size : 7
Loop 7 : Capacity : 16, Factor : 12, Current Size : 8
Loop 8 : Capacity : 16, Factor : 12, Current Size : 9
Loop 9 : Capacity : 16, Factor : 12, Current Size : 10
Loop 10 : Capacity : 16, Factor : 12, Current Size : 11
Loop 11 : Capacity : 16, Factor : 12, Current Size : 12
Loop 12 : Capacity : 32, Factor : 24, Current Size : 13

虽然我无法肯定地解释,但是来自HashMap实现的以下片段表明哈希计算值可能会增加Map的大小。

void More addEntry(int hash, K key, V value, int **bucketIndex**) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
    if (size++ >= threshold)
        resize(2 * table.length);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM