目录
HashMap介绍
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap采取数组加链表的存储方式来实现,数组里的每个元素都是单向链表( Entry(key, value, next) ),
我们都知道数组查询效率高,但是增删效率低,而链表增删效率高,而查询效率低;也正是因为这一特性,HashMap采用的是数组加链表的结构;
不过,数组结构存储区间连续,占用内存情况较为严重,而链表结构存储区间较为离散,占有内存比较宽松;所以在JDK1.8之后,HashMap引入了红黑树的结构,当链表长度超过一定值(默认为8)时,则会将链表结构进行调整转变为红黑树结构;当红黑树中的元素数量小于8个时,则又会将结构体进行转变为链表结构;
HashMap的默认初始容量为16,即在创建一个HashMap时,HashMap首先创建一个长度为16的空数组,当我们插入数据时,会根据插入的Key进行hash计算,得出当前数据应该存储在数组的链表位置下标。
官方的hash函数运算代码:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
简单一点讲,HashMap就是一个Entry数组,
private Entry<K, V>[] table;
每一个Entry包含一个key-value键值对(Entry就是HashMap里实现的一个静态内部类,里面定义了key、value等属性)
static class Entry<K, V> implements Map.Entry<K,V> {
private final long hash;
private final K key;
private V value;
Entry<K,V> next;
Entry(long hash, K key, V value, Entry<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
大致的数据结构如下图(是不是很像一个表格):
基础的Bean属性:
Hash:当前位置Key的Hash值
Key:当前位置的键值
Value:当前位置存储的值
Next:下一个Entry对象
开始实现
实现方向:根据key的Hash值,计算得到Entry<K, V>数组的索引值,(这里的hash值理解成每个Key的hash都是固定的值就可以了)
取出第一个Entry对象,通过equals方法判断key是否相同,相同返回,不相同则取next值(下一个Entry对象)继续比较;直到相等或者next为空时返回最终结果
下面自定义实现CustomHashMap的基本功能:
/**
* @description {自定义HashMap}
* @author xxxxx
* @version 1.0
* <p>
* Create by xxxxxx on 2020/12/15 15:00.
*/
public class CustomHashMap<K, V> implements Map<K, V>, Serializable {
//默认容量
private static int defaultLength = 16;
//默认加载因子
private static double defualtLoaderFactor = 0.75;
private Entry<K, V>[] table;
//键值对的数量
private int size = 0;
/**
* 构造一个空的HashMap,指定的初始*容量和负载系数
* @param length
* @param loaderFactor
*/
public CustomHashMap(int length, double loaderFactor) {
defaultLength = length;
defualtLoaderFactor = loaderFactor;
table = new Entry[defaultLength];
}
public CustomHashMap() {
this(defaultLength, defualtLoaderFactor);
}
/* ---------------- Static utilities -------------- */
/**
* 根据Key计算HashCode值
* @param key
* @return
*/
private int hash(K key) {
int m = defaultLength;
int i = Objects.hashCode(key) % m;
return i > 0 ? i : -i;
}
/**
* 将Entry定义为成员内部类,定义Key-Value的存储结构,作为基本的Hash节点;
* @param <K>
* @param <V>
*/
static class Entry<K, V> implements Map.Entry<K,V> {
private final long hash;
private final K key;
private V value;
//存储指向下一个Entry的引用,单链表结构
Entry<K,V> next;
Entry(long hash, K key, V value, Entry<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
return this.value = value;
}
@Override
public String toString() {
return "{key=" + key + ", value=" + value + ", nextKey="+ ((next != null) ? next.key : null) +"}";
}
}
@Override
public V put(K key, V value) {
int index = hash(key);
//根据key的hash值,数组长度计算该Entry<key,value>的数组下标
Entry<K,V> entry = table[index];
if(entry == null) {
table[index] = new Entry<>(index, key, value, null);
size ++;
}else {
//如果Key相同,新的Entry会覆盖旧的Entry
table[index] = new Entry<>(index, key, value, entry);
}
return table[index].getValue();
}
@Override
public V get(Object key) {
K k = (K) key;
int index = hash(k);
if(table[index] == null) {
return null;
}
return findValue(table[index], k);
}
@Override
public V remove(Object key) {
K k = (K) key;
int index = hash(k);
if (table[index] != null){
removeEntry(table[index], k);
}
return null;
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
}
@Override
public void clear() {
}
@Override
public Set<K> keySet() {
return null;
}
@Override
public Collection<V> values() {
return null;
}
@Override
public Set<Map.Entry<K, V>> entrySet() {
return null;
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size <= 0;
}
@Override
public boolean containsKey(Object key) {
K k = (K) key;
int index = hash(k);
if(table[index] != null){
V value = findValue(table[index], k);
return !(null == value);
}
return false;
}
/**
* 查找Value只能一个个链表去遍历
* @param value
* @return
*/
@Override
public boolean containsValue(Object value) {
V v;
Entry<K, V>[] tab = table;
if(tab != null && size > 0){
for (int i = 0; i < tab.length; ++i) {
for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
if ((v = e.value) == value || (value != null && value.equals(v)))
return true;
}
}
}
return false;
}
@Override
public String toString() {
return "CustomHashMap{" +
"table=" + Arrays.toString(table) +
"\n, size=" + size +
'}';
}
/**
* 根据Key递归查找指定的Entry
* @param entry
* @param key
* @return
*/
public V findValue(Entry<K, V> entry, K key){
if(Objects.equals(key, entry.key)){
return entry.value;
}else{
if (!Objects.isNull(entry.next)){
return findValue(entry.next, key);
}
}
return null;
}
/**
* 递归Entry,删除指定的Key
* @param entry
* @param key
* @return
*/
public V removeEntry(Entry<K, V> entry, K key){
if(Objects.equals(entry.key, key)){
}
return null;
}
测试后发现有key计算后的hash值过大,超出默认的容量造成下标越界