纯手写实现HashMap
1.hashmap的实现
① 初始化
1)定义一个Node<K, V>的数组来存放元素,但不立即初始化,在使用的时候再加载
2)定义数组初始大小为16
3)定义负载因子,默认为0.75,
4)定义size用来记录容器存放的元素数量
② put的实现思路
1) 判断容器是否为空,为空则初始化。
2)判断容器的size是否大于阀值,是的话就扩容为以前长度的两倍,并重新计算其中元素的存放位置,进行重新存放
3)计算出key的index角标位置
4)判断计算出的index位置是否存在元素,存在的话则遍历链表,判断key是否存在,存在则更新,不存在则增加
③ get的实现思路
1)通过key计算出它所在的index
2)遍历index位置处的链表,并获取value返回。
- package com.test;
- /**
- * 自定义hashMap
- * @author cf
- *
- * @param <K>
- * @param <V>
- */
- public class MyHashMap<K, V> implements MyMap<K, V>{
- //1.定义一个容器用来存放元素, 但不立即初始化,使用懒加载方式
- Node<K, V>[] table = null;
- //2.定义容器的默认大小
- static int DEFAULT_INITIAL_CAPACITY = 16;
- //3.HashMap默认负载因子,负载因子越小,hash冲突机率越低,综合结论得出0.75最为合适
- static final float DEFAULT_LOAD_FACTOR = 0.75f;
- //4.记录当前容器实际大小
- static int size;
- @SuppressWarnings("unchecked")
- @Override
- public V put(K k, V v) {
- //1.判断容器是否为空为空则初始化。
- if (table == null) {
- table = new Node[DEFAULT_INITIAL_CAPACITY];
- }
- //如果size大于阈值则进行扩容
- if (size > DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR) {
- resize();
- }
- //2.计算出index角标
- int index = getIndex(k, DEFAULT_INITIAL_CAPACITY);
- //3.将k-v键值对放进相对应的角标,如果计算出角标相同则以链表的形势存放
- Node<K, V> node = table[index];
- if (node == null) {
- table[index] = new Node<>(k, v, null);
- size ++;
- return table[index].getValue();
- } else {
- Node<K, V> newNode = node;
- //循环遍历每个节点看看是否存在相同的key
- while (newNode != null) {
- //这里要用equals 和 == 因为key有可能是基本数据类型,也有可能是引用类型
- if (k.equals(newNode.getKey()) || k == newNode.getKey()) {
- newNode.setValue(v);
- size ++;
- return v;
- }
- newNode = node.getNextNode();
- }
- table[index] = new Node<K, V>(k, v, table[index]);
- size ++;
- return table[index].getValue();
- }
- }
- /**
- * 获取index
- * @param key
- * @param length
- * @return
- */
- public int getIndex(K key, int length) {
- int hashCode = key.hashCode();
- int index = hashCode % length;
- return index;
- }
- /**
- * 获取key
- */
- @Override
- public V get(K k) {
- int index = getIndex(k, DEFAULT_INITIAL_CAPACITY);
- Node<K, V> node = table[index];
- if (k.equals(node.getKey()) || k == node.getKey()) {
- return node.getValue();
- } else {
- Node<K, V> nextNode = node.getNextNode();
- while(nextNode != null) {
- if (k.equals(nextNode.getKey()) || k == nextNode.getKey()) {
- return nextNode.getValue();
- }
- }
- }
- return null;
- }
- /**
- * 对size进行扩容
- */
- @SuppressWarnings("unchecked")
- public void resize() {
- //1.创建新的table长度扩展为以前的两倍
- int newLength = DEFAULT_INITIAL_CAPACITY * 2;
- Node<K, V>[] newtable = new Node[newLength];
- //2.将以前table中的取出,并重新计算index存入
- for (int i = 0; i < table.length; i++) {
- Node<K, V> oldtable = table[i];
- while (oldtable != null) {
- //将table[i]的位置赋值为空,
- table[i] = null;
- //方法1:重新计算index,然后按照put时候的方法进行放值,此种方法会不停的new 对象会造成效率比较低
- /*K key = oldtable.getKey();
- int index = getIndex(key, newLength);
- newtable[index] = new Node<K, V>(key, oldtable.getValue(), newtable[index]);
- oldtable = oldtable.getNextNode();*/
- //方法2:
- //计算新的index值
- K key = oldtable.getKey();
- int index = getIndex(key, newLength);
- //将以前的nextnode保存下来
- Node<K, V> nextNode = oldtable.getNextNode();
- //将newtable的值赋值在oldtable的nextnode上,如果以前是空,则nextnode也是空
- oldtable.setNextNode(newtable[index]);
- newtable[i] = oldtable;
- //将以前的nextcode赋值给oldtable以便继续遍历
- oldtable = nextNode;
- }
- }
- //3.将新的table赋值回老的table
- table = newtable;
- DEFAULT_INITIAL_CAPACITY = newLength;
- newtable = null;
- }
- @Override
- public int size() {
- return size;
- }
- @SuppressWarnings("hiding")
- class Node<K, V> implements Entry<K, V> {
- private K key;
- private V value;
- private Node<K, V> nextNode; //下一节点
- public Node(K key, V value, Node<K, V> nextNode) {
- super();
- this.key = key;
- this.value = value;
- this.nextNode = nextNode;
- }
- @Override
- public K getKey() {
- return this.key;
- }
- @Override
- public V getValue() {
- return this.value;
- }
- @Override
- public void setValue(V value) {
- this.value = value;
- }
- public Node<K, V> getNextNode() {
- return nextNode;
- }
- public void setNextNode(Node<K, V> nextNode) {
- this.nextNode = nextNode;
- }
- public void setKey(K key) {
- this.key = key;
- }
- //判断是否还有下一个节点
- /*private boolean hasNext() {
- return true;
- }*/
- }
- // 测试方法.打印所有的链表元素
- public void print() {
- for (int i = 0; i < table.length; i++) {
- Node<K, V> node = table[i];
- System.out.print("下标位置[" + i + "]");
- while (node != null) {
- System.out.print("[ key:" + node.getKey() + ",value:" + node.getValue() + "]");
- node = node.nextNode;
- }
- System.out.println();
- }
- }
- }
2.测试代码
- package com.test;
- public class TestMap {
- public static void main(String[] args) {
- MyHashMap<String, String> extHashMap = new MyHashMap<String, String>();
- extHashMap.put("1号", "1号");//
- extHashMap.put("2号", "1号");//
- extHashMap.put("3号", "1号");//
- extHashMap.put("4号", "1号");//
- extHashMap.put("6号", "1号");//
- extHashMap.put("7号", "1号");
- extHashMap.put("14号", "1号");
- extHashMap.put("22号", "1号");
- extHashMap.put("26号", "1号");
- extHashMap.put("27号", "1号");
- extHashMap.put("28号", "1号");
- extHashMap.put("66号", "66");
- extHashMap.put("30号", "1号");
- System.out.println("扩容前数据....");
- extHashMap.print();
- System.out.println("扩容后数据....");
- extHashMap.put("31号", "1号");
- extHashMap.put("66号", "123466666");
- extHashMap.print();
- // 修改3号之后
- System.out.println(extHashMap.get("66号"));
- }
- }
如有疑问或错误请在下方留言指出! 谢谢大佬
纯手写实现HashMap的更多相关文章
- springmvc 动态代理 JDK实现与模拟JDK纯手写实现。
首先明白 动态代理和静态代理的区别: 静态代理:①持有被代理类的引用 ② 代理类一开始就被加载到内存中了(非常重要) 动态代理:JDK中的动态代理中的代理类是动态生成的.并且生成的动态代理类为$Pr ...
- 简易-五星评分-jQuery纯手写
超级简单的评分功能,分为四个步骤轻松搞定: 第一步: 引入jquery文件:这里我用百度CDN的jquery: <script src="http://apps.bdimg.com/l ...
- vue10行代码实现上拉翻页加载更多数据,纯手写js实现下拉刷新上拉翻页不引用任何第三方插件
vue10行代码实现上拉翻页加载更多数据,纯手写js实现下拉刷新上拉翻页不引用任何第三方插件/库 一提到移动端的下拉刷新上拉翻页,你可能就会想到iScroll插件,没错iScroll是一个高性能,资源 ...
- 超级简单的jQuery纯手写五星评分效果
超级简单的评分功能,分为四个步骤轻松搞定: 第一步: 引入jquery文件:这里我用百度CDN的jquery: <script src="http://apps.bdimg.com/l ...
- 纯手写Myatis框架
1.接口层-和数据库交互的方式 MyBatis和数据库的交互有两种方式: 使用传统的MyBatis提供的API: 使用Mapper接口: 2.使用Mapper接口 MyBatis 将配置文件中的每一个 ...
- 3 手写Java HashMap核心源码
手写Java HashMap核心源码 上一章手写LinkedList核心源码,本章我们来手写Java HashMap的核心源码. 我们来先了解一下HashMap的原理.HashMap 字面意思 has ...
- SQL纯手写创建数据库到表内内容
建表啥的只点点鼠标,太外行了,不如来看看我的纯手写,让表从无到有一系列:还有存储过程临时表,不间断的重排序: 一:建数据库 create Database Show on primary ( name ...
- 纯手写SpringMVC到SpringBoot框架项目实战
引言 Spring Boot其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置. 通过这种方式,springboot ...
- 纯手写SpringMVC架构,用注解实现springmvc过程
1.第一步,首先搭建如下架构,其中,annotation中放置自己编写的注解,主要包括service controller qualifier RequestMapping 第二步:完成对应的anno ...
随机推荐
- “全栈2019”Java第三十八章:类与方法
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- [BZOJ1935][SHOI2007]Tree 园丁的烦恼(树状数组)
题目描述 很久很久以前,在遥远的大陆上有一个美丽的国家.统治着这个美丽国家的国王是一个园艺爱好者,在他的皇家花园里种植着各种奇花异草. 有一天国王漫步在花园里,若有所思,他问一个园丁道: “最近我在思 ...
- Jmeter后置处理器之JSON Extractor
一.使用场景 json extractor后置处理器用在返回格式为json的HTTP请求中,用来获取返回的json中的某个值.并保存成变量供后面的请求进行调用或断言等. 二.使用方法 步骤一:选择HT ...
- es去重查询
{ "query": { "bool": { "must": ...
- leetcode-806-Number of Lines To Write String
题目描述: We are to write the letters of a given string S, from left to right into lines. Each line has ...
- Codeforces Round #556 题解
Codeforces Round #556 题解 Div.2 A Stock Arbitraging 傻逼题 Div.2 B Tiling Challenge 傻逼题 Div.1 A Prefix S ...
- 基础篇:6.4)形位公差-基准 Datum
本章目标:了解形位公差基准及运用. 1.定义: 基准 — 与被测要素有关且用来定义其几何位置关系的一个几何理想要素(如轴线.直线.平面等): — 可由零件上的一个或多个基准要素构成. 模拟基准要素 ...
- crontab例行性共作
一.单一工作调度 at [-mldv] TIME at -c 工作号码 -m:当at工作结束后,即是没有输出信息,以email通知用户该工作已完成 -l:at -l相当于atq,列出目前系统上所有的a ...
- ORACLE INSERT ALL 用法
1INSERT ALL 1.1句法 multi_table_insert :: = conditional_insert_clause :: = 1.2multi_table_insert 在多表插入 ...
- Python+Selenium之常用模块
要用webdriver:from selenium import webdriver eg: driver = webdriver.Chrome() driver.maximize_wind ...