HashMap底层原理及简单实现

♀尐吖头ヾ 提交于 2020-02-04 23:46:50

HashMap可以用键值对来存储对象,所谓键值对,就是可以通过对象来寻找对象,是对数组索引的推广。

底层原理

存储结构

HashMap底层的实现采用了哈希表,基本结构是“数组+链表”。HashMap里面有一个叫table的Entry数组,Entry是一个用作链表节点的类,也就是说,数组的每个元素都对应着一个链表,结构如图所示:
在这里插入图片描述

存储过程

在这里插入图片描述

当调用HashMap的put方法时

  • 首先,会计算key对象的哈希码,也就是调用它的hashcode()方法,一般是根据地址求出的。
  • 然后,通过特定的算法计算出哈希码对应在 [0,数组长度-1) 区间内的哈希值,例如图中是让哈希码模16,求出对应的哈希值是15;
  • 最后在table数组对应位置的链表中添加value对象(在添加的过程中会比较,将相同key值的对象覆盖掉)

注:两种最极端的计算哈希值算法:1.hashCode / hashCode 退化成一个链表 2.hashCode / 1退化成一个数组

注:为了提高效率,会将模运算换成位运算,当length是2的幂次时,hashCode & (length - 1)等效于hashCode % length

注:JDK8中,当链表长度大于8时,会将链表转化成一颗红黑树,提高查找效率

查找键值对过程

查找的过程类似于存储过程,只不过把存改为找到后返回。

  • 根据key值求出hashCode
  • 根据hashCode求出哈希值
  • 在table数组对应位置的链表中找到key值相同(equals)的节点,返回value对象
    注:equals为true,则hashCode必相同

扩容

当数组使用了四分之三的长度时,会进行扩容,扩容为原来长度的两倍,实质上是新定义一个两倍长度的数组,再进行数组拷贝。

简单实现

只是对HashMap的简单实现,许多情况并未考虑,仅仅是为了方便理解HashMap的两个核心方法:get()和put();

节点类

/**
 * 用于HashMap
 * TODO
 * @version 1.0
 * @author 王星宇
 */
class node<K,V>{
	int hash;
	K key;
	V value;
	node next;
}

变量及构造方法

node[] table; //位桶数组  buckey array
	int size;
	/**
	 * 构造方法 ,默认16(2的整数次幂)个
	 */
	myHashMap(){
		table = new node[16] ; // 2 的整数次幂
	}

计算哈希值

/**
	 * 计算v的哈希值
	 * @param key  key的hashCode
	 * @param length 数组长度
	 * @return v的哈希值
	 */
	public int myHash(int key,int length) {
		return key & (length - 1);
	}

重写toString()方法

/**
	 * 重写toString方法
	 */
	@Override
	public String toString() {
		StringBuffer SB = new StringBuffer("[");
		for(int i = 0;i < table.length;i++) {
			node temp = table[i];
			while(temp != null) {
				SB.append(temp.key + "=" + temp.value + ",");
				temp = temp.next;
			}
		}
		SB.setCharAt(SB.length() - 1, ']');
		
		return SB.toString();
	}

*put()方法

/**
	 * 存放键值对,未考虑数组扩容
	 * @param key 
	 * @param value
	 */
	public void put(K key,V value) {
		node newNode = new node();
		newNode.hash = myHash(key.hashCode(),table.length);
		newNode.key = key;
		newNode.value = value;
		newNode.next = null;
		
		node temp = table[newNode.hash];
		node last = null;//链表最后一个节点
		if(temp == null) {
			table[newNode.hash] = newNode;
			size++;
		}else {
			while(temp != null) {
				if(temp.key.equals(key)) {
					temp.value = value;
					break;
				}else {
					last = temp;	
					temp = temp.next;
				}
			}
			if(temp == null) {
				last.next = newNode;
				size++;
			}
		}
	}

get()方法

public V get(K key) {
		int hash = myHash(key.hashCode(),table.length);
		V value = null;
		
		if(table[hash] != null) {
			node temp = table[hash];
			while(temp != null) {
				if(temp.key.equals(key)){
					value = (V)temp.value;
					break;
				}
				temp = temp.next;
			}
		}
		
		return value;
	}
	

全部代码

package myCollection;
/**
 * 测试HashMap
 * TODO
 * @version 1.0
 * @author 王星宇
 */
public class myHashMap <K,V>{
	node[] table; //位桶数组  buckey array
	int size;
	/**
	 * 构造方法 ,默认16(2的整数次幂)个
	 */
	myHashMap(){
		table = new node[16] ; // 2 的整数次幂
	}
	/**
	 * 存放键值对,未考虑数组扩容
	 * @param key 
	 * @param value
	 */
	public void put(K key,V value) {
		node newNode = new node();
		newNode.hash = myHash(key.hashCode(),table.length);
		newNode.key = key;
		newNode.value = value;
		newNode.next = null;
		
		node temp = table[newNode.hash];
		node last = null;//链表最后一个节点
		if(temp == null) {
			table[newNode.hash] = newNode;
			size++;
		}else {
			while(temp != null) {
				if(temp.key.equals(key)) {
					temp.value = value;
					break;
				}else {
					last = temp;	
					temp = temp.next;
				}
			}
			if(temp == null) {
				last.next = newNode;
				size++;
			}
		}
	}
	/**
	 * 计算v的哈希值
	 * @param key  key的hashCode
	 * @param length 数组长度
	 * @return v的哈希值
	 */
	public int myHash(int key,int length) {
		return key & (length - 1);
	}
	/**
	 * 重写toString方法
	 */
	@Override
	public String toString() {
		StringBuffer SB = new StringBuffer("[");
		for(int i = 0;i < table.length;i++) {
			node temp = table[i];
			while(temp != null) {
				SB.append(temp.key + "=" + temp.value + ",");
				temp = temp.next;
			}
		}
		SB.setCharAt(SB.length() - 1, ']');
		
		return SB.toString();
	}
	
	public V get(K key) {
		int hash = myHash(key.hashCode(),table.length);
		V value = null;
		
		if(table[hash] != null) {
			node temp = table[hash];
			while(temp != null) {
				if(temp.key.equals(key)){
					value = (V)temp.value;
					break;
				}
				temp = temp.next;
			}
		}
		
		return value;
	}
	
}
/**
 * 用于HashMap
 * TODO
 * @version 1.0
 * @author 王星宇
 */
class node<K,V>{
	int hash;
	K key;
	V value;
	node next;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!