学习目标:
HashMap浅析学习内容:
1、 我们从使用的角度来展开
首先hashmap的结构如下图所示(数组加链表的数据结构)。
一般我们都是如下面使用hashmap
HashMap map1 = new HashMap();
上面是无参构造函数,看一下调用了啥?
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
//这是给一个默认的扩展因子
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
还有一个这样的使用方式
HashMap map2 = new HashMap(15, 0.75f);
上面是有参构造函数,看一下调用了啥?
/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and load factor.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
//扩展因子
this.loadFactor = loadFactor;
//数组的总长度
this.threshold = tableSizeFor(initialCapacity);
}
/**
* Returns a power of two size for the given target capacity.
*/
//确定了我们hashmap的长度
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
通过这个构造函数我们初始化了hashmap最大长度以及扩展因子,接下来就聊聊hash函数的实现方式,还是看一下源码。
可以从get的时候入手
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
//n-1 & hash 这样就只保留了hashCode,以这个值作为数组的下标
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
从上面的代码我们可以发现hash(Object key)
这个就是hashmap的hash算法,就两行,
比如 : key.hashCode : 0000000000001100 0000000000001011 (32位)
>> 16 : 0000000000000000 0000000000001100 (32位)
return : 0000000000001100 0000000000000111 (32位)
而计算我们这个node存放下标的时候 会把这个hash值在进行处理n -1 & hash
这样就会取到 0111,算出来位置就是7,node就会在table[7]的地方插入。插入的时候也会遇到当前位置已经被别人占用的时候,就会采用头插法(插入的时候处理比较复杂),当链表长度大于8时,就会把当前链表变成红黑树。
看了一会儿代码就来实现一下自己的hashMap(无红黑树,非线程安全)
package com.suyong.lib;
import java.util.HashMap;
public class MyHashMap<K, V> implements AbstractHashMap<K, V> {
//最大长度
static final int MAXIMUM_CAPACITY = 1 << 30;
//扩展因子
private float loadFactor = 0.75f;
//默认长度(16)
private int threshold;
//HashMap table
private Node[] table = null;
//记录实际的节点个数
private int size = 0;
public MyHashMap() {
threshold = 1 << 4;
}
public MyHashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
//列如:13 ->0000 1101
//计算后长度 -> 0001 0000(16)
//列如:25 ->0001 1001
//计算后长度 -> 0010 0000(32)
//计算HashMap长度
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
//hash算法
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
@Override
public V put(K key, V value) {
synchronized (HashMap.class) {
if (table == null) {
table = new Node[threshold];
}
}
//达到扩容因子的限制个数,开始扩容,防止查找性能降低。
if (size >= (threshold * loadFactor)) {
//扩容重新分配大小
resize();
}
int index = hash(key) & (threshold - 1);
Node<K, V> node = table[index];
if (node == null) {
node = new Node<K, V>(key, value, null);
size++;
} else {
Node<K, V> newNode = node;
while (newNode != null) {
if (newNode.getKey().equals(key)) {
//找到既存的key,就个更新value
return newNode.setValue(value);
} else {
if (newNode.next == null) {
//头插法
node = new Node<K, V>(key, value, node);
size++;
}
}
newNode = newNode.next;
}
}
table[index] = node;
return null;
}
//扩容
private void resize() {
threshold = threshold << 1;
Node<K, V>[] newTable = new Node[threshold];
for (int i = 0; i < table.length; i++) {
Node<K,V> oldNode = table[i];
while(oldNode!=null) {
Node oldNodeNext = oldNode.next;
int index = hash(oldNode.getKey()) & (threshold - 1);
newTable[index] = oldNode;
oldNode.next = newTable[index];
newTable[index] = oldNode;
oldNode = oldNodeNext;
}
table[i] = null;
}
table = newTable;
threshold = newTable.length;
newTable = null;
}
@Override
public V get(K key) {
Node<K, V> node = getNode(table[hash(key.hashCode()) & (threshold - 1)], key);
return node.value;
}
private Node<K, V> getNode(Node<K, V> node, K k) {
while (node != null) {
if (node.getKey().equals(k) || node.getKey() == k) {
return node;
}
node = node.next;
}
return null;
}
@Override
public int size() {
return size;
}
private final class Node<K, V> implements Entry<K, V> {
private K key;
private V value;
private Node<K, V> next;
public Node(K key, V value, Node<K, V> next) {
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) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
}
}
public interface AbstractHashMap<K,V>{
V put(K key,V value);
V get(K key);
int size();
interface Entry<K,V>{
K getKey();
V getValue();
V setValue(V value);
}
}
附上插入时的流程图