如果各位对不熟悉JAVA的 HashMap原理和实现,那么这篇文章可能值得一看。
HashMap 简介: 基于哈希表的 Map 接口的非同步实现。允许使用null值和null键。键不允许重复,值允许重复。存储是无序的,是按照哈希散列排序的。底层数据结构:Hash链表。
图示:
一 :实现原理(结合JDK源码片段):
1.初始化HashMap : 更具给定的参数初始化一个数据类型为Node<K,V>的table数组。
transient Node<K,V>[] table;
2.存入键值对<K,V>:
2.1 判断该table是否为空,若为空则初始化该table。
2.2 判断该key是否为null,若为null ,则将该<K,V>放置到数组的第一个位置。否则继续。
2.3 先使用hash(k) 计算得到该k对应的hash数值。
//计算key的 hash数值 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
2.4 使用该hash 对整个表的长度进行取模运算,以求该k能放在数组中的坐标I 。
tab[i = (n - 1) & hash]
2.5判断table[i]处是否有数据,若不为空,则遍历该位置的链表,比较是否有相同的key,如果有相同的key则用的新的V覆盖旧的V,否则在该链表的顶部插入该节点。
判断是否为同一个key:
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
3.获取key对应的value。步骤和存入数据差不多。
二.MyHashMap的代码实现(肯定有不足之处):
import java.io.Serializable; import java.util.*; //zhaoke 2018-1-8 public class MyHashMap<K, V> implements Cloneable, Serializable { //设置初始的容量为16 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //设置其最大容量为1024 static final int MAXIMUM_CAPACITY = 1 << 30; //设置其容量阈值因子为0.75 static final float DEFAULT_LOAD_FACTOR = 0.75f; //实际储存的大小 transient int size; //阈值 int threshold; //设置每一个节点 实现 Map.Entry<K,V> 是为了实现一个链表 static class Node<K, V> implements Map.Entry<K, V> { final int hash; final K key; V value; MyHashMap.Node<K, V> next; Node(int hash, K key, V value, MyHashMap.Node<K, V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } public final K getKey() { return key; } public final V getValue() { return value; } public final String toString() { return key + "=" + value; } public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); // 调用 Objects的hashCode() 进行异或计算 } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public final boolean equals(Object o) { if (o == this) return true; if (o instanceof Map.Entry) { Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue())) return true; } return false; } } //用一个数据类型为Node 的 数组充当 MyHashMap private Node<K, V>[] table; transient int modCount; //计算key的 hash数值 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } //初始化MyHashmap public MyHashMap() { initTable(); } //将数据插入MyHashMap中 public V put(K key, V value) { //如果是空的,则进行初始化大小 if (table == null) { initTable(); } //允许K/V存放null //若key为null则调用putForNullKey方法 放在数组第一位置 if (key == null) return putNullKey(value); //计算hash int hash = hash(key); //搜索指定hash值在对应table中的索引。 int i = indexFor(hash, table.length); //如果i处不为空,则循环遍历下一个元素,产生链表 for (Node<K, V> n = table[i]; n != null; n = n.next) { Object k; if (n.hash == hash && ((k = n.key) == key || key.equals(k))) { V oldValue = n.value; n.value = value; return oldValue; } } //如果i处索引为null或没有相同的,则表明还没有Entry modCount++; //插入,将k v插入到i处 addNode(hash, key, value, i); return null; } void addNode(int hash, K key, V value, int bucketIndex) { //若大小不够就扩充到原来的2倍 if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); } //创建新Node createNode(hash, key, value, bucketIndex); } void createNode(int hash, K key, V value, int bucketIndex) { //获取指定索引处的Node Node<K, V> n = table[bucketIndex]; //将新创建的 Node放入 bucketIndex 索引处,并让新的 Entry 指向原来的 Entry table[bucketIndex] = new Node<>(hash, key, value, n); //增加大小 size++; } //初始化table数组 private void initTable() { table = new Node[DEFAULT_INITIAL_CAPACITY]; //初始化阈值 threshold=(int) Math.floor(DEFAULT_INITIAL_CAPACITY*DEFAULT_LOAD_FACTOR); } //只放空数值 private V putNullKey(V value) { Node oldnode = table[0]; Node node = new Node<K, V>(0, null, value, null); table[0] = node; return (oldnode == null) ? null : ((V) oldnode.getValue()); } //返回对应key的 在数组的位置 取代模 % 取余运算 static int indexFor(int h, int length) { // length 必须为 2 的N 次 return h & (length - 1); } //获取该MyHashMap的size public int size() { return size; } //判断该MyHashMap是否为空 public boolean isEmpty() { return size == 0; } //重新修改起容量 新的容量 void resize(int newCapacity) { //引用扩容前的Node数组 Node[] oldTable = table; int oldCapacity = oldTable.length; //扩容前的数组大小如果已经达到最大(2^30)了 1G if (oldCapacity == MAXIMUM_CAPACITY) { //修改阈值为int的最大值(2^31-1),这样以后就不会扩容了 threshold = Integer.MAX_VALUE; return; } //初始化一个新的Node数组 Node[] newTable = new Node[newCapacity]; //将数据转移到新的Node数组里 transfer(newTable); //将新table 赋值给原来的table table = newTable; threshold = (int)(newCapacity * DEFAULT_LOAD_FACTOR);//修改阈值 } void transfer(Node[] newTable) { //oldtable 引用原来的table Node[] oldtable = table; int newCapacity = newTable.length; //遍历原来的table for (int j = 0; j < oldtable.length; j++) { //取得旧oldtable数组的每个元素 Node<K,V> n = oldtable[j]; if (n != null) { //释放我们可爱的oldtable oldtable[j] = null; do { Node<K,V> next = n.next; //!!重新计算每个元素在数组中的位置 int i = indexFor(n.hash, newCapacity); //此处注意 越最近添加的k 对应的节点 其距离 数组越进 可以理解为链表的头插 n.next = newTable[i]; //将元素放在数组上 newTable[i] = n; //访问下一个Node链上的元素 n = next; } while (n != null); } } } //获取数值 public V get(K key) { if (key == null) return getForNullKey(); Node<K,V> node = getNode(key); return null == node ? null : node.getValue(); } public Node getNode(K key){ //如果是空的 则返回null if (table == null) { return null; } int hash = hash(key); //搜索指定hash值在对应table中的索引。 int i = indexFor(hash, table.length); //如果i处不为空,则循环遍历下一个元素 for (Node<K, V> n = table[i]; n != null; n = n.next) { Object k; if (n.hash == hash && ((k = n.key) == key || key.equals(k))) { return n; } } return null; } //获取key为Null的value public V getForNullKey(){ return (table[0]==null)?null:(table[0].value); } }
文章代码肯定有不足之处,各位老铁一定要私信我哟。