程序员们,在jdk1.7中HashMap有个致命错误,你们知道吗?

JAVA 2023-07-05 17:29:38
59阅读

  序言

  沒有bug的程序猿并不是好程序员,是否这一大道理。

  如果没有bug,哪来的发展室内空间;

  如果没有bug,哪来的维护费;

  如果没有bug,哪来的程序流程升級;

  ......

  就算是大家每天用的高手们写的java最底层都是会有系统漏洞,更何况大家这种小人物呢。

  程序猿们,在jdk1.7中HashMap有一个严重错误-循环链表,大家知道吗?

  jdk1.7 HashMap框架图

  jdk1.7是数组 链表的构造

  

  jdk1.7版本号中关键存有2个难题

  • 头插法会导致循环链表的状况
  • 链表太长,会造成 查看高效率降低
  • 应用尾插法,清除出現循环链表的状况
  • 链表太长后,转换为红黑树,提升 查看高效率

  线程同步另外put时,假如另外启用了resize实际操作,很有可能会造成 循环链表造成,从而促使后边get的情况下,会无限循环。下边详尽论述循环链表怎样产生的。

  resize函数

  数组扩充函数,关键的作用便是建立扩充后的新数组,而且将启用transfer函数将旧数组中的原素转移到新的数组

  void resize(int newCapacity)

  {

  Entry[] oldTable = table;

  int oldCapacity = oldTable.length;

  ......

  //建立一个新的Hash Table

  Entry[] newTable = new Entry[newCapacity];

  //将Old Hash Table上的数据备份转移到New Hash Table上

  transfer(newTable);

  table = newTable;

  threshold = (int)(newCapacity * loadFactor);

  }

  transfer函数

  transfer逻辑性实际上也简易,解析xml旧数组,将旧数组原素根据头插法的方法,转移到新数组的相匹配部位难题出就出在头插法。

  void transfer(Entry[] newTable)

  {

  //src旧数组

  Entry[] src = table;

  int newCapacity = newTable.length;

  for (int j = 0; j < src.length; j ) {

  Entry<K,V> e = src[j];

  if (e != null) {

  src[j] = null;

  do {

  Entry<K,V> next = e.next;

  int i = indexFor(e.hash, newCapacity);

  e.next = newTable[i];

  newTable[i] = e;

  e = next;

  } while (e != null);//因为是链表,因此 有一个循环系统全过程

  }

  }

  }

  static int indexFor(int h, int length){

  return h&(length-1);

  }

  下边举个具体事例

  //下边详尽表述必须采用这些编码,因此 先型号,将一下编码分成五个流程

  do {

  1、Entry<K,V> next = e.next;

  2、int i = indexFor(e.hash, newCapacity);

  3、e.next = newTab[i];

  4、newTable[i] = e;

  5、e= next;

  } while(e != null)

  刚开始HashMapHashMapHashMap容积设成2,载入阀值为2∗0.75=12*0.75=12∗0.75=1

  

  进程T2 和进程T1另外插进原素,因为阀值为1,因此 都必须启用resize函数,开展扩充实际操作

  

  进程T1先堵塞于编码Entry<K,V> next = e.next;,以后进程T2 实行完扩充实际操作

  

  以后进程T1被唤起,执行,进行一次循环系统后

  刚开始e→3,next→7, 实行下边编码后,e→7, next→3

  2、int i = indexFor(e.hash, newCapacity);

  3、e.next = newTab[i];

  4、newTable[i] = e;

  5、e= next;

  1、Entry<K,V> next = e.next;

  

  进程 T1 实行第二次循环系统后

  刚开始e→7,next→3, 实行下边编码后,e→3, next→null

  2、int i = indexFor(e.hash, newCapacity);

  3、e.next = newTab[i];

  4、newTable[i] = e;

  5、e= next;

  1、Entry<K,V> next = e.next;

  

  进程T1实行第三次循环系统后,产生无限循环

  刚开始e→3,next→null, 实行下边编码后,e→null

  2、int i = indexFor(e.hash, newCapacity);

  3、e.next = newTab[i];

  4、newTable[i] = e;

  5、e= next;

  1、Entry<K,V> next = e.next;

  

  倘若你实行get(11), 11%4=3, 深陷无限循环

  这个问题只存有于JDK1.7中,在JDK1.8中应用了不一样的扩充完成方法,促使不容易出現这类状况。

  HashMap 则应用了拉锁式的散列优化算法,在 JDK 1.8 中导入了红黑树提升太长的链表。

  算法设计平面图以下:

  

the end
免责声明:本文不代表本站的观点和立场,如有侵权请联系本站删除!本站仅提供信息存储空间服务。