实现自己的HashMap- 2019-09-21 20:35:53- java
- 基础: hashmap,java,实现自己的HashMap
HsahMap1.7jdk原理分析:https://www.jianshu.com/p/068b6d25b593
HashMap中重要的概念:
初始化容量直: 1 << 4 map的初始化容量
最大容量值:1 << 30 map可容纳的最大容量
扩容因子 : 0.75 即容量已达到该设置条件,put时会进行扩容
扩容阈值:扩容阈值=容量(table的size)扩容因子
size:map中存储的键值对的数量
table:存储实体
实际扩容因子:创建时可由有参构造给出
实际容量:创建时可由有参构造给出
扩容次数:可限制扩容
key允许为null,存储时key为Null,在table中的下标为0.取值时从table[0]中获取链表,查找链表中k为null的节点。
HashMap使用数组+链表的形式实现的,通过计算key的hash值获取key在数组中对应的下标,添加新的节点到该下标的链表中。
取值时,通过计算key的hash值获取key在数组中对应的下标,获取该下标上的链表,通过eques方法从链表中找到对应的value。
以下是代码实现:
public class MyHashMap<K, V> {/*** 初始化容量 1的4次方*/private final Integer DEFAULT_INITIAL_CAPACITY = 1 << 4;/*** 最大容量 1的30次方 超过最大量将被最大量所赋值*/private final Integer MAXIMUM_CAPACITY = 1 << 30;/*** 实际容量*/private Integer capacity;/*** 默认扩容因子 四分之三*/private final float DEFAULT_LOAD_FACTOR = 0.75f;/*** 实际扩容因子*/private float loadFactor;/*** 扩容阈值=容量(table的size)*扩容因子*/private int threshold;/*** 实际存储*/private MyEntry<K, V>[] table = null;/*** map 的size 既Map中键值对的数量*/private static int size = 0;/*** 扩容次数:可做扩容限制*/private static int resizeIndex = 0;/*** 无参构造*/public MyHashMap() {//实际扩容因子this.loadFactor = DEFAULT_LOAD_FACTOR;//实际容量this.capacity = DEFAULT_INITIAL_CAPACITY;//扩容阈值this.threshold = tableSizeFor(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);//初始化tablethis.table = new MyEntry[DEFAULT_INITIAL_CAPACITY];}/*** @param initialCapacity 初始化容量* @param loadFactor 扩容因子*/public MyHashMap(int initialCapacity, float loadFactor) {//如果初始化容量大于默认最大容量 则等于最大容量if (initialCapacity >= MAXIMUM_CAPACITY) {initialCapacity = MAXIMUM_CAPACITY;}//实际扩容因子this.loadFactor = loadFactor;//实际容量this.capacity = initialCapacity;//计算扩容阈值this.threshold = tableSizeFor(initialCapacity, loadFactor);//初始化tablethis.table = new MyEntry[initialCapacity];}/*** @param initialCapacity 初始化容量*/public MyHashMap(int initialCapacity) {if (initialCapacity >= MAXIMUM_CAPACITY) {initialCapacity = MAXIMUM_CAPACITY;}//实际容量this.capacity = initialCapacity;//实际扩容因子this.loadFactor = DEFAULT_LOAD_FACTOR;//扩容阈值this.threshold = tableSizeFor(initialCapacity, loadFactor);//初始化tablethis.table = new MyEntry[initialCapacity];}public V get(K k) {if (null == table) {return null;}if (null == k) {return getForNullKey();}//计算k对应的hash值,根据hash计算在table中对应下标int hash = k.hashCode();int index = getIndex(hash, capacity);MyEntry<K, V> entry = table[index];while (null != entry) {//hash值相同 同时key相同if (hash == entry.getHash() && (k == entry.getK() || k.equals(entry.getK()))) {return entry.getV();}entry = entry.getNext();}return null;}/*** 获取key为null时的值** @return*/private V getForNullKey() {MyEntry<K, V> entry = table[0];while (null != entry) {if (null == entry.getHash() && null == entry.getK()) {return entry.getV();}entry = entry.getNext();}return null;}/*** 添加数据** @param k* @param v* @return*/public V put(K k, V v) {//put时扩容resize();//key==null时if (null == k) {return putForNullKey(k, v);}//计算k的hashint hash = k.hashCode();//计算k在数组中对应的下标int index = getIndex(hash, capacity);//获取对应下标上的Entry:若新增时 则为下个节点MyEntry<K, V> nextEntry = table[index];for (MyEntry<K, V> entry = table[index]; entry != null; entry = entry.getNext()) {//判断是否已经存在对应的keyif (hash == entry.getHash() && (k == entry.getK() || k.equals(entry.getK()))) {V oldValue = entry.getV();entry.setV(v);return oldValue;}}size++;//新增table[index] = new MyEntry<>(k, v, nextEntry, hash);//新增时返回Nullreturn null;}/*** 存储key为null的值** @param k* @return*/private V putForNullKey(K k, V v) {MyEntry<K, V> entry = table[0];//判断是否已经存在key为null的键值对,如果存在则替换while (null != entry) {if (null == entry.getHash() && null == entry.getK()) {V oldValue = entry.getV();entry.setV(v);entry.setNext(entry);return oldValue;}entry = entry.getNext();}//新增size++;table[0] = new MyEntry<>(k, v, entry, null);return null;}/*** 计算k在数组中对应的下标** @param hash hash值* @param capacity table容量* @return*/private int getIndex(int hash, int capacity) {//table的容量int index = hash % capacity;return index >= 0 ? index : -index;}/*** 扩容*/private void resize() {//已是最大扩容,不再扩容if (capacity >= MAXIMUM_CAPACITY) {//将阈值设置为Inter的最大容量threshold = Integer.MAX_VALUE;return;}//未达到最大阈值 无需扩容if (size < threshold) {return;}int newCapacity = capacity * 2;if (newCapacity >= MAXIMUM_CAPACITY) {newCapacity = MAXIMUM_CAPACITY;}//扩容次数resizeIndex++;//重新计算扩容阈值threshold = tableSizeFor(newCapacity, loadFactor);//新容量capacity = newCapacity;MyEntry<K, V>[] oldEntry = table;//创建新数组MyEntry<K, V>[] newEntry = new MyEntry[newCapacity];//将旧数组的值转移到新数组transition(oldEntry, newEntry);//重新定义tabletable = newEntry;}/*** 转移数组上的数据** @param oldEntries* @param newEntries*/private void transition(MyEntry<K, V>[] oldEntries, MyEntry<K, V>[] newEntries) {for (int i = 0; i < oldEntries.length; i++) {MyEntry<K, V> entry = oldEntries[i];while (null != entry) {MyEntry<K, V> next = entry.getNext();//扩容之后重新计算在数组中的下标int index = getIndex(entry.getK().hashCode(), capacity);//将元素放在数组上://采用单链表的头插入方式 = 在链表头上存放数据 = 将数组位置的原有数据放在后1个指针、将需放入的数据放到数组位置中// 即 扩容后,可能出现逆序:按旧链表的正序遍历链表、在新链表的头部依次插入entry.setNext(newEntries[index]);//赋值到新数组newEntries[index] = entry;entry = next;}}}/*** 计算扩容阈值*/private int tableSizeFor(int initialCapacity, float loadFactor) {BigDecimal init = new BigDecimal(initialCapacity);BigDecimal bigLoad = new BigDecimal(loadFactor);BigDecimal multiply = init.multiply(bigLoad);return multiply.intValue();}}
* entry组成了链表public final class MyEntry<K, V> {private K k;private V v;private MyEntry<K, V> next;private Integer hash;public MyEntry(K k, V v, MyEntry<K, V> next, Integer hash) {this.k = k;this.v = v;this.next = next;this.hash = hash;}public MyEntry() {}public MyEntry(K k, V v) {this.k = k;this.v = v;}public MyEntry(K k, V v, MyEntry<K, V> next) {this.k = k;this.v = v;this.next = next;}public Integer getHash() {return hash;}public void setHash(Integer hash) {this.hash = hash;}public K getK() {return k;}public void setK(K k) {this.k = k;}public V getV() {return v;}public void setV(V v) {this.v = v;}public MyEntry<K, V> getNext() {return next;}public void setNext(MyEntry<K, V> next) {this.next = next;}}
* 最后测试一下:@Testpublic void demo() {MyHashMap<String, String> map = new MyHashMap();System.out.println("-------------------MyHashMap begin-------------------------");Long t1 = System.currentTimeMillis();for (int i = 0; i < 1000; i++) {map.put("key" + i, "value" + i);}for (int i = 0; i < 1000; i++) {String value = map.get("key" + i);System.out.println("key: " + "key" + i + "---" + "value: " + value);assert (value.equals("value" + i));}//null keymap.put(null, "value:key is null");System.out.printf(map.get(null));Long t2 = System.currentTimeMillis();System.out.println("MyHashMap耗时:" + (t2 - t1));System.out.println("-------------------MyHashMap end-------------------------");Map<String, String> hashMap = new HashMap();Long t3 = System.currentTimeMillis();for (int i = 0; i < 1000; i++) {hashMap.put("key" + i, "value" + i);}for (int i = 0; i < 1000; i++) {System.out.println("key: " + "key" + i + "---" + "value: " + hashMap.get("key" + i));}Long t4 = System.currentTimeMillis();System.out.println("JDK的HashMap耗时:" + (t4 - t3));}
**
