HashMap做为一个常常用到的类,先今后类开始阅读。java
####存储原理图 node
####1、 成员变量数组
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
默认的桶数量app
static final int MAXIMUM_CAPACITY = 1 << 30;
桶的最大数量ui
static final float DEFAULT_LOAD_FACTOR = 0.75f;
默认负载系数this
static final int TREEIFY_THRESHOLD = 8;
桶内元素树化的最少个数spa
static final int UNTREEIFY_THRESHOLD = 6;
反树化时, 桶内元素的最多个数code
final float loadFactor;
设置的负载系数对象
static final int MIN_TREEIFY_CAPACITY = 64;
桶内元素就行树化的时候,整个容器的须要达到的最小容量继承
transient int modCount;
容器内部发生结构性改变的次数(用于iterator遍历)
transient int size;
容器内部存储的元素个数
transient Node<K,V>[] table;
存储桶的数组,这个是HashMap的核心
int threshold;
进行再次内存分配的时候。元素须要达到的个数
transient Set<Map.Entry<K,V>> entrySet;
提供map的访问接口
####2、公有方法
public void clear()
清空内部元素。
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
获取与key对应的value,而且将它们做为参数调用remappingFunction(),获得新的value。
若是key原来不存在的话,将新的key,value添加进去,可是value不能为空值。
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
根据key去查找node. 分为如下几种状况 :
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
根据key去查找node,若node存在,则使用mappingFunction(key)去更新值,不存在马上返回。
public boolean containsKey(Object key)
根据key去查找node,调用内部的getNode方法
public V put(K key, V value)
将键值对放入bucket内部,此处注意可能要进行treeify。
public boolean containsValue(Object value)
遍历table,而且遍历每一个bucket
public Set<Map.Entry<K,V>> entrySet()
返回一个EntrySet类型对象,EntrySet类型继承自AbstractSet,咱们主要用这个类来进行对HashMap的遍历
public void forEach(BiConsumer<? super K, ? super V> action)
针对每个内部存储的元素,调用action.accept()方法,在调用过程当中,modCount不能改变,这就是为何HashMap的迭代是failFast的,若是改变就会抛出异常。
public V get(Object key)
获取key对应的value
public V getOrDefault(Object key, V defaultValue)
JDK8新方法,若是value为null,返回defaultValue。
public boolean isEmpty()
返回size == 0
public Set<K> keySet()
返回一个包含全部key的Set,能够对这个set进行迭代,而后遍历
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
经过key去查询一个已存在的键值对,而且将oldValue与newValue传递给remappingFunction进行计算后从新赋值给对应key
public V put(K key, V value)
将键值对存入map内,涉及到resize以及treeify
public void putAll(Map<? extends K, ? extends V> m)
经过entrySet()遍历m,将m内的全部元素插入新的map
public V putIfAbsent(K key, V value)
若是key对象的键值对不存在,则存入新的键值对
public V remove(Object key)
经过key去检索键值对,而且删除
public boolean remove(Object key, Object value)
经过key去检索键值对,而且经过equals方法比较检索出来的value,若value相等则移除
public boolean replace(K key, V oldValue, V newValue)
若是key对应的map内的value为oldValue,则将其替换为newValue
public V replace(K key, V value)
强key对象的值替换为value
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
对于全部的键值对调用function,apply()方法,并将返回值做为新的value
public int size()
返回大小
public Collection<V> values()
获取全部的值的Collection(可是并非说把全部的值存储在了相似于List这样的集合里面了,咱们只是能够调用Collection的接口而已)
####3、简单实现
package com.braon.jdk; public class SimpleHashMap<K, V> { private Node<K, V>[] table; private int capacity = 4; private float loadFactor = 0.75f; private int threshold = 3; private int size; public SimpleHashMap() { init(); } public SimpleHashMap(int capacity, int loadFactor) { this.capacity = capacity; this.loadFactor = loadFactor; this.threshold = capacity * loadFactor; init(); } @SuppressWarnings("unchecked") private void init() { capacity = 1 << 4; table = new Node[capacity]; size = 0; } public void clear() { init(); } public void put(K K, V V) { if (K == null || V == null) { return; } // space is narrow else if (size + 1 > threshold) { resize(); } Node<K, V> newNode = new Node<>(); newNode.setK(K); newNode.setV(V); newNode.setHash(K.hashCode() & (capacity - 1)); newNode.setNext(null); if (putNode(newNode)) size++; } public V get(K K) { // calculate store place int hash = K.hashCode() & (capacity - 1); Node<K, V> first = table[hash]; if (first != null) { do { if (first.getK().equals(K)) return first.getV(); } while ((first = first.next) != null); return null; } // if else return null; } @SuppressWarnings("unchecked") private void resize() { // recaculate the array size capacity = capacity << 1; threshold = (int) (loadFactor * capacity); // keep the old one, and renew a new table Node<K, V>[] oldTable = table; table = new Node[capacity]; // relay the elements for (Node<K, V> first : oldTable) { while (first != null) { first.setHash(first.getK().hashCode() & (capacity - 1)); this.putNode(first); // we need to remove the link Node<K, V> next = first.next; first.next = null; first = next; } // while } // for } public int getSize() { return size; } public int getCapacity() { return capacity; } private boolean putNode(Node<K, V> newNode) { Node<K, V> first = table[newNode.getHash()]; if (first == null) { table[newNode.getHash()] = newNode; } else { while (first.next != null) { // two absolute equivalent K, we need to update the V if (first.getK().equals(newNode.getK())) { first.setV(newNode.getV()); return false; } first = first.next; } if (first.getK().equals(newNode.getK())) { first.setV(newNode.getV()); return false; } else first.next = newNode; } return true; } public void print() { System.out.println(toString() + "\r\n"); } public void remove(K k) { if (k == null) return; int hash = k.hashCode() & (capacity - 1); Node<K, V> prev = table[hash]; Node<K, V> cur = table[hash].next; if (prev.getK().equals(k)) { table[hash] = table[hash].next; size--; return; } while (cur != null) { if (cur.getK().equals(k)) { prev.next = cur.next; size--; return; } prev = cur; cur = cur.next; } } public String toString() { StringBuffer sb = new StringBuffer(); for (Node<K, V> node : table) { if (node != null) { do { sb = sb.append("hashCode: " + node.getHash() + ", K: " + node.getK() + ", V: " + node.getV() + " "); } while ((node = node.next) != null); sb.append("\r\n"); } } return sb.toString(); } } class Node<K, V> { Node<K, V> next; private K k; private V v; private int hash; public Node() { } public Node(K k, V v) { this.k = k; this.v = v; } public int getHash() { return hash; } public void setHash(int hash) { this.hash = hash; } public K getK() { return k; } public void setK(K k) { this.k = k; } public Node<K, V> getNext() { return next; } public void setNext(Node<K, V> next) { this.next = next; } public V getV() { return v; } public void setV(V v) { this.v = v; } }
####注意点 一、须要同时重载equals和hashCode方法 二、size增加后又减少,可是capacity不会减少 三、使用entrySet()进行遍历,比较快,使用keySet()遍历,相对较慢 四、hashCode方法的编写须要注意,最好不要有多个值产生相同的hashCode,否则对查询产生影响 五、loadfactor最好不要改变 六、查询次数较多,插入次数相对较少,且元素hashCode相对集中,请使用TreeHashMap 七、values()方法获取的返回值并无存储了任何一个value的引用,只是咱们能够用Collection接口的方法去调用这个返回值而已 八、耗时较多的插入通常都是由于须要resize或者treeify