Java HashMap集合深度分析

本文深入探讨了Java中HashMap的工作原理,特别是其内部实现机制,包括负载因子、容量调整、put和get方法的具体实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Java的全世界里,无论是类仍是各种数据,其构造的处置是整个程序的逻辑以及性能的要害。因为本人接触了一个相关性能与逻辑与此同时依存的问题,于是就开始研究这方面的问题。找遍了大大很小论坛,也把《Java 杜撰机轨范》,《apress,.java.collections.(2001),.bm.ocr.6.0.shareconnector》,和《Thinking in Java》翻了也找不到良好的答案,于是乎一怒之下把JDK的 src 解压出来研究,扩然开豁,遂写此文,和大伙分享体味和就便印证我懂得再有没有破绽。 这边就拿HashMap来研究吧。

  HashMap堪称JDK的一大实用工具,把各个Object照射起来,兑现了“键--值”对应的高速存取。但现实里边做了些什么呢?

  在这头里,先引见一下子负荷因数和容量的属性。大伙都晓得实则一个 HashMap 的现实容量便 因数*容量,其默认值是 16×0.75=12; 这个很主要,对效率很一定影响!应惠存HashMap的对象超过这个容量时,HashMap 就会从新结构存取表。这便是一个大问题,我后头缓缓引见,横竖,如其你已经晓得你大略要寄放多少个对象,最好设为该现实容量的能接受的数目字。

  两个要害的步骤,put和get:

  先有这么一个概念,HashMap是宣言了 Map,Cloneable, Serializable 接口,和承继了 AbstractMap 种,里边的 Iterator 实则重要都是其内门类HashIterator 和其余几个 iterator 种兑现,当然再有一个很主要的承袭了Map.Entry 的 Entry 内类型,因为大伙儿都有源代码,大伙儿有兴趣可以见见这一部分,我重要想说明的是 Entry 内门类。它包孕了hash,value,key 和next 这四个属性,很主要。put的源码如次

  public Object put(Object key, Object value) {
  Object k = maskNull(key);

  这个便是判断键值是不是为空,并不很高深,实则如若为空,它会回来一个static Object 作为键值,这乃是为啥HashMap容许空键值的缘故。

  int hash = hash(k);
  int i = indexFor(hash, table.length);

  这接续的两步乃是 HashMap 最牛群的地方!研究完我都汗颜了,内中 hash 便是经过 key 这个Object的 hashcode 开展 hash,其后透过 indexFor 取得在Object table的目录值。

  table???不用惊诧,实则HashMap也神人不到哪里去,它乃是用 table 来置的。最牛群的乃是用 hash 会准确的回来目录。此中的hash算法,我和JDK的笔者 Doug 联系过,他提议我见见《The art of programing vol三》可憎的是,我先头就一直在觅,我都找不到,他这么一提,我就愈加急了,遗憾衣袋空空啊!!!

  不知道大伙儿有没有留心 put 其实是一个有回来的步骤,它会把雷同键值的 put 覆盖掉并回来旧的值!如次步骤彻底说了然 HashMap 的构造,实则便是一个表加上在呼应位置的Entry的链表:

  for (Entry e = table[i]; e != null; e = e.next) {
  if (e.hash == hash && eq(k, e.key)) {
  Object oldvalue = e.value;
  e.value = value; //把新的值给予给对应键值。
  e.recordAccess(this); //空步骤,留待兑现
  return oldvalue; //回来雷同键值的对应的旧的值。
  }
  }
  modCount++; //结构性改动的次数
  addEntry(hash, k, value, i); //平添新元素,关键所在!
  return null; //没雷同的键值回到
  }

  我们把要害的步骤拿出来分析:

  void addEntry(int hash, Object key, Object value, int bucketIndex) {
  table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);

  由于 hash 的算法有或许令不同的键值有雷同的hash码并有雷同的table目录,如:key=“33”和key=Object g的hash都是-8901334,那它通过indexfor以后的目录一建都为i,这么在new的时分这个Entry的next就会指向这个正本的table[i],还有下一个也如斯,形成一个链表,和put的循环对订e.next取得旧的值。到这边,HashMap的构造,大伙儿也非常清楚了吧?

  if (size++ >= threshold) //这个threshold乃是能现实容纳的量
  resize(二 * table.length); //超出这个容量就会将Object table复建

  所谓的复建也不神人,便是建一个两倍大的table(我在别的论坛上看到有人说是两倍增一,把我骗了),其后再一个个indexfor进入!注意!!这乃是效率!!如其你能让你的HashMap不需要复建那么屡次,效率会大大提高!

  说到这边也差不离了,get比put容易得多,大伙,理解put,get也差不已几多了。至于collections我是以为,它是合适宽泛的,应不完全适宜特有的,如若大伙的程序亟需非一般的用场,自各儿写吧,实则很简单。(起草人是这么跟我说的,他还提议我用LinkedHashMap,我看了源码之后发现,LinkHashMap实则乃是承袭HashMap的,其后override呼应的步骤,有兴趣的同仁,自个儿looklook)筑个 Object table,写呼应的算法,便ok啦。

  举个事例吧,像 Vector,list 啊什么的实则都很简单,至多就多了的同步的宣言,实则如其要兑现像Vector那种,安插,剔除未几的,可以用一个Object table来兑现,按目录存取,平添等。

  如若安插,剔除比较多的,可以建两个Object table,其后每个元素用带有next构造的,一个table存,如若要安插到i,但是i已经有元素,用next连起来,其后size++,并在另一个table记要其位置。

我的异常网|异常|exception

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值