纯手写实现HashMap
Posted chenfei-java
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了纯手写实现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号");// 0 extHashMap.put("2号", "1号");// 1 extHashMap.put("3号", "1号");// 2 extHashMap.put("4号", "1号");// 3 extHashMap.put("6号", "1号");// 4 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的主要内容,如果未能解决你的问题,请参考以下文章
Java集合相关学习——手写一个简单的Map接口实现类(HashMap)
Java集合相关学习——手写一个简单的Map接口实现类(HashMap)