请说一下HashMap的底层原理。。。相信很多小伙伴都从面试官那里通过这句话。本文将带领大家一步步脱掉HashMap的外衣,写一个自己的HashMap
放在前面:什么是hash?

首先HashMap绝对不是一个简单的存储键值对的集合,如果面试的时候回答键值对基本上凉一半,,,正确答案是:HashMap是一个线性链表结构,由数组和链表共同构建而成。
如图:

可以看到纵向的是数组,横向的是链表,数组和链表有机结合组成了HashMap。
这里有一个问题:为什么要使用数组和链表?大家还记不记得数组和链表的特性,数组查询速度快存取速度不行,链表增删速度快查找慢,那么有没有一种结构能够查询和存取速度都相对理想,不像数组和链表这么偏科呢?HashMap!
HashMap是这么存储的:先计算(K,V)中的K的哈希值,然后对(数组长度-1)取摩确定要存储的纵向数组上的位置,这里横向上是一个链表结构。首先判断该链表否为空,为空就放进去并使next指针指向null,不为空时就利用临时变量把新来的元素插到原数据的前面。这样HashMap的存储就完成了。那么HashMap是怎么存储的大家肯定心里多多少少都有点逼数了吧。同样先使用K计算后先找出纵向位置,然后比较K是否与当前地址一致,一致就取出,不一致就next,直至找出该元素。这样兼顾数组与链表的长处查的时候使用数组特性多一点,取得时候使用链表。这里我们想一下,如果横向链表过长,当查询时是不是充分利用了链表低效率的查询特性。。。。那么是不是应该控制这种横向链表的无益增长,怎么做,答案是使用负载因子 一旦超出临界值就扩容,然后重新装配。

说了这么多,到底该怎么实现一个HashMap?上干货
这里就不一步步解释了,大家如果有问题可以在下面留言
第一步,定义接口:

package com.david;public interface Map<K,V> {//向HashMap中插入值public V put(K k,V c);//根据Key获取HashMap中的值public V get(K k);//获取集合中的元素个数public int size();//获取几何中,键值对的对象interface  Entry<K,V>{K getKey();V getValue();V setValue(V v);}
}

第二步实现接口:

package com.david;public class HashMap<K, V> implements Map<K, V> {//数据存储结构=》 数组加链表 Node<K, V>[] array = null;//数组/hash桶的初始长度private static int defaultLength = 16;//加载因子/扩容因子private static double factor = 0.75D;//集合中的元素个数private int size;//调试打印函数public void print() {System.out.println("***************");if (array != null) {Node<K, V> node = null;for (int i = 0; i < array.length; i++) {node = array[i];System.out.print("下标【" + i + "】");while (node != null) {System.out.print("[" + node.getKey() + ":" + node.getValue() + "]");if (node != null)node = node.next;elsenode = null;}System.out.println();}}}//put元素方法@Overridepublic V put(K k, V v) {//1、懒加载机制,使用的时候进行分配if (array == null) {array = new Node[defaultLength];}//2、通过hash算法,计算出具体插入的位置int index = position(k, defaultLength);//判断是否需要扩容if (size > defaultLength * factor) {resize();}//3、加入要放入的元素Node<K, V> node = array[index];if (node == null) {array[index] = new Node<K, V>(k, v, null);size++;} else {if (k.equals(node.getKey()) || k == node.getKey()) {return node.setValue(v);} else {array[index] = new Node<K, V>(k, v, node);size++;}}return null;}//扩容,并重新排列元素private void resize() {//翻倍扩容//1、创建新temp arrayNode<K, V>[] temp = new Node[defaultLength << 1];//2、重新计算散列值,插入到新的array中。code=key%(defaultLength-1) ---》code=key%(defaultLength*2-1)Node<K, V> node = null;for (int i = 0; i < array.length; i++) {node = array[i];while (node != null) {//重新散列int index = position(node.getKey(), temp.length);Node<K, V> next = node.next;node.next = temp[index];temp[index] = node;node = next;}}//替换掉老的arrayarray = temp;defaultLength = temp.length;temp = null;}private int position(K k, int Length) {int code = k.hashCode();//取模算法return code % (Length - 1);//求与算法//return code & (defaultLength-1);}@Overridepublic V get(K k) {if (array != null) {int index = position(k, defaultLength);//获取对应哈希桶节点Node<K, V> node = array[index];//遍历链表while (node != null) {//返回对应key的valueif (node.getKey() == k)return node.getValue();elsenode = node.next;}}return null;}@Overridepublic int size() {return 0;}static class Node<K, V> implements Map.Entry<K, V> {K key;V value;Node<K, V> next;public Node(K key, V value, Node<K, V> next) {this.key = key;this.value = value;this.next = next;}@Overridepublic K getKey() {return this.key;}@Overridepublic V getValue() {return this.value;}@Overridepublic V setValue(V v) {V oldValue = this.value;this.value = v;return oldValue;}}
}

第三步:测试

import com.david.HashMap;public class Main {public static void main(String[] args) {HashMap<String, String> map = new HashMap<String, String>();for (int i = 1; i < 50; i++) {map.put("0" + i + "号", "0" + i);}map.print();System.out.println("--->" + map.get("01号"));}
}

手撕HashMap(原理)相关推荐

  1. 手撕HashMap数据结构(带你逐行阅读源码)

    目录 概述 原理简述 新旧版本对比 结构设计原理 继承关系 成员变量 核心:为什么负载因子设定为0.75? 核心:为什么树化的链表阈值是8? 核心:为什么树退化的链表阈值是6? 构造函数 默认构造方法 ...

  2. 【数字IC手撕代码】Verilog奇数分频|题目|原理|设计|仿真(三分频,五分频,奇数分频及特殊占空比)

    芯片设计验证社区·芯片爱好者聚集地·硬件相关讨论社区·数字verifier星球 四社区联合力荐!近500篇数字IC精品文章收录! [数字IC精品文章收录]学习路线·基础知识·总线·脚本语言·芯片求职· ...

  3. Python3《机器学习实战》学习笔记(八):支持向量机原理篇之手撕线性SVM

    原 Python3<机器学习实战>学习笔记(八):支持向量机原理篇之手撕线性SVM 置顶 2017年09月23日 17:50:18 阅读数:12644 转载请注明作者和出处: https: ...

  4. 【数字IC手撕代码】Verilog偶数分频|题目|原理|设计|仿真(二分频,四分频,六分频,八分频,偶数分频及特殊占空比)

    芯片设计验证社区·芯片爱好者聚集地·硬件相关讨论社区·数字verifier星球 四社区联合力荐!近500篇数字IC精品文章收录! [数字IC精品文章收录]学习路线·基础知识·总线·脚本语言·芯片求职· ...

  5. 【数字IC手撕代码】Verilog固定优先级仲裁器|题目|原理|设计|仿真

    芯片设计验证社区·芯片爱好者聚集地·硬件相关讨论社区·数字verifier星球 四社区联合力荐!近500篇数字IC精品文章收录! [数字IC精品文章收录]学习路线·基础知识·总线·脚本语言·芯片求职· ...

  6. 手撕公司SSO登陆原理

    点击上方蓝色字体,选择"标星公众号" 优质文章,第一时间送达 上一篇:这300G的Java资料是我师傅当年给我的,免费分享给大家 下一篇:这200G的Java实战资料是我师傅当年教 ...

  7. 【数字IC手撕代码】Verilog自动售卖饮料机|题目|原理|设计|仿真

    芯片设计验证社区·芯片爱好者聚集地·硬件相关讨论社区·数字verifier星球 四社区联合力荐!近500篇数字IC精品文章收录! [数字IC精品文章收录]学习路线·基础知识·总线·脚本语言·芯片求职· ...

  8. 【数字IC手撕代码】Verilog伪随机数生成器|线性反馈移位寄存器|题目|原理|设计|仿真

    芯片设计验证社区·芯片爱好者聚集地·硬件相关讨论社区·数字verifier星球 四社区联合力荐!近500篇数字IC精品文章收录! [数字IC精品文章收录]学习路线·基础知识·总线·脚本语言·芯片求职· ...

  9. 【数字IC手撕代码】Verilog半整数分频|题目|原理|设计|仿真

    芯片设计验证社区·芯片爱好者聚集地·硬件相关讨论社区·数字verifier星球 四社区联合力荐!近500篇数字IC精品文章收录! [数字IC精品文章收录]学习路线·基础知识·总线·脚本语言·芯片求职· ...

最新文章

  1. 不一样的命令行 – Windows PowerShell简介
  2. 页面切换语言包使用session不用cookie
  3. sql 忽略大小写_Flink使用Calcite解析Sql做维表关联(一)
  4. CPU和GPU的区别
  5. 详解C与C++的联系与区别
  6. 爬虫解析利器PyQuery详解及使用实践
  7. dedeCMS 静态页面的倒计时插件(原生VS自定义)
  8. android前端开发 布局学习
  9. .NET Framework3.0答疑
  10. 安装 pear、phpunit 测试用例步骤方法
  11. less 使用小结!笔记!
  12. Hadoop中的一些基本操作
  13. java实现凯撒密码_Java实现进阶版凯撒密码
  14. python 最优解 ma_python遗传算法求最优解
  15. 【计算机网络】—网络初识01
  16. 阿里云Oss水印图片处理Utils
  17. ASR项目实战-架构设计
  18. DataGrid 嵌套应用
  19. python requests常见用法总结
  20. 前端面试之ES5与ES6的区别

热门文章

  1. halcon学习和实践(第一个范例threshold.hdev)
  2. python算法工程师面试_算法工程师的面试经历及总结
  3. 测绘资质申请条件及办理资质要求
  4. Android 录制桌面视频 screenrecord
  5. Ubuntu 中文字体设置备忘
  6. 美国在PC处理器市场的垄断被ARM打破,国产处理器也取得了突破
  7. 日记 or 小小说 :想进腾讯的师弟师妹们,别学我 (二)
  8. 身份证提取生日和性别
  9. 毛玻璃效果(CSS)实现
  10. 网易云直播SDK使用总结