hashmap原理说明
hashmap是存放键值对的一种数据结构。底层采用数组、链表、红黑树实现。根据插入的key的hashcode与该hashcode向右移16位的值进行异或运算,再与存放键值对的数组长度减一进行取余操作确定该键值对的存放位置。当存放位置发生冲突的时候则键值对串成链表,当链表长度大于八的时候,链表转化为红黑树。数组的优点就是查找速度快,缺点就是数组的大小初始化的就必须确定,增删元素的时候必须挪动剩余元素。链表的优点就是增删很快,不用挪动剩余元素,但是查找元素要从头开始遍历。
map接口
package com.lv.blog.testothers;
public interface MyMap<K,V> {
//往map中加入值
V put(K key,V value);
//键值对的接口
interface MyEntry<K,V>{
}
}
hashmap实现
package com.lv.blog.testothers;
public class MyMapImp<K,V> implements MyMap<K,V> {
//初始化容量
static final int CAPACITY = 16;
//初始化负载因子
static final float LOAD_FACTOR = 0.75f;
//数组保存键值对对象
transient MyNode<K,V>[] table;
//放入值
@Override
public V put(Object key, Object value) {
//创建一个数组保存键值对对象
return putval(hash(key),key,value);
}
//根据hash放入值
final V putval(int hash, Object key, Object value) {
//tab临时变量存储键值对数组,p代表对应数组下标的值,n代表数组长度,i代表根据hash生成对数组下标
MyNode<K,V>[] tab; MyNode<K,V> p; int n,i;
//如果键值对数组没有创建或者长度为零,则创建键值对数组;
if((tab = table) == null || ((n = tab.length) == 0)) {
//resize方法创建初始数组
n = (tab = resize()).length;
}
//容量减一与hash码的异或运算生成数组下标,根据数组下标找到对应的键值对对象。如果为空,则创建键值对对象并且放入数组中。
if((p = tab[i = (n - 1) & hash] ) == null){
tab[i] = new MyNode(hash,key,value,null);
}
return null;
}
//根据初始化容量初始化键值对数组
final MyNode<K,V>[] resize() {
MyNode<K,V>[] oldTab = table;
int newCap = 0;
//获取存在对键值对数组对初始化容量
int oldCap = (oldTab == null) ? 0 : oldTab.length;
if(oldCap == 0){
newCap = CAPACITY;
}
//创建初始化数组
MyNode<K,V>[] newTab = (MyNode<K,V>[])new MyNode[newCap];
return newTab;
}
//这样hash的原理参考:https://blog.csdn.net/qq_42034205/article/details/90384772
//大概意思是:int是32位的,一般16位就能代表绝大多数的map容量,所以直接后16位进行计算不能使值更加分散
//所以让前16位后移16位,与低16位进行异或运算使更加均衡。
static final int hash(Object key){
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//实现Entry类
static class MyNode<K,V> implements MyMap.MyEntry<K,V>{
final int hash;
final K key;
V value;
MyNode<K,V> next;
MyNode(int hash,K key,V value,MyNode<K,V> next){
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
}