美文网首页
HashMap源码分析

HashMap源码分析

作者: 阿桃_28e7 | 来源:发表于2018-05-23 10:04 被阅读0次

public class HashMap

extends AbstractMap

implements Map, Cloneable, Serializable

{

/**

* The default initial capacity - MUST be a power of two.

*/

static final int DEFAULT_INITIAL_CAPACITY = 16;

/**

* The maximum capacity, used if a higher value is implicitly specified

* by either of the constructors with arguments.

* MUST be a power of two <= 1<<30.

*/

static final int MAXIMUM_CAPACITY = 1 << 30;

/**

* The load factor used when none specified in constructor.

*/

static final float DEFAULT_LOAD_FACTOR = 0.75f;

/**

* The table, resized as necessary. Length MUST Always be a power of two.

*/

transient Entry[] table;

/**

* The number of key-value mappings contained in this map.

*/

transient int size;

/**

扩容临界点【当size>threshold,将resize】

* The next size value at which to resize (capacity * load factor).

* @serial

*/

int threshold;

/**

总结:当负载因子较大时,【threshold较大】,去给table数组扩容的可能性就会少,所以相对占用内存较少(空间上较少),但是每条entry链上的元素会相对较多,查询的时间也会增长(时间上较多)。反之就是,负载因子较少的时候,【threshold较大】,给table数组扩容的可能性就高,那么内存空间占用就多,但是entry链上的元素就会相对较少,查出的时间也会减少。所以才有了负载因子是时间和空间上的一种折中的说法。所以设置负载因子的时候要考虑自己追求的是时间还是空间上的少。

* The load factor for the hash table.

*

* @serial

*/

final float loadFactor;

/**

* The load factor for the hash table.

*

* @serial

*/

final float loadFactor;

//HashMap多线程处理之 Fail-Fast机制:http://www.cnblogs.com/alexlo/archive/2013/03/14/2959233.html

/**

* The number of times this HashMap has been structurally modified

* Structural modifications are those that change the number of mappings in

* the HashMap or otherwise modify its internal structure (e.g.,

* rehash).  This field is used to make iterators on Collection-views of

* the HashMap fail-fast.  (See ConcurrentModificationException).

*/

transient int modCount;

//内存可见性:通俗来说就是,线程A对一个volatile变量的修改,对于其它线程来说是可见的,即线程每次获取volatile变量的值都是最新的。

    /** Float.isNaN()

*static public boolean isNaN(float v) {

*        return (v != v);

*    }

*/

/**

* Constructs an empty HashMap with the specified initial

* capacity and the default load factor (0.75).

*

* @param  initialCapacity the initial capacity.

* @throws IllegalArgumentException if the initial capacity is negative.

*/

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);

//这里做了一个移位运算,保证了初始容量一定为2的幂,假如你传的是5,那么最终的初始容量为8

//设置initCapacity的时候,尽量设置为2的幂,这样会去掉计算比initCapactity大,且为2的幂的数的运算

// Find a power of 2 >= initialCapacity

int capacity = 1;

while (capacity < initialCapacity)

capacity <<= 1;

this.loadFactor = loadFactor;

threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);

table = new Entry[capacity];

useAltHashing = sun.misc.VM.isBooted() &&

(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);

init();

}

/**

* Constructs an empty HashMap with the specified initial

* capacity and the default load factor (0.75).

*/

public HashMap(int initialCapacity) {

this(initialCapacity, DEFAULT_LOAD_FACTOR);

}

/**

* Constructs an empty HashMap with the default initial capacity

* (16) and the default load factor (0.75).

*/

public HashMap() {

this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);

}

public HashMap(Map m) {

this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,

DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);

putAllForCreate(m);

}

private void putAllForCreate(Map m) {

for (Map.Entry e : m.entrySet())

putForCreate(e.getKey(), e.getValue());

}

private void putForCreate(K key, V value) {

int hash = null == key ? 0 : hash(key);

int i = indexFor(hash, table.length);

/**

* Look for preexisting entry for key.  This will never happen for

* clone or deserialize.  It will only happen for construction if the

* input Map is a sorted map whose ordering is inconsistent w/ equals.

*/

for (Entry e = table[i]; e != null; e = e.next) {

Object k;

if (e.hash == hash &&

((k = e.key) == key || (key != null && key.equals(k)))) {

e.value = value;

return;

}

}

createEntry(hash, key, value, i);

}

void createEntry(int hash, K key, V value, int bucketIndex) {

Entry e = table[bucketIndex];

table[bucketIndex] = new Entry<>(hash, key, value, e);

size++;

}

static class Entry implements Map.Entry {

final K key;

V value;

Entry next;

int hash;

/**

* Creates new entry.

*/

Entry(int h, K k, V v, Entry n) {

value = v;

next = n;

key = k;

hash = h;

}

/**

* Retrieve object hash code and applies a supplemental hash function to the

* result hash, which defends against poor quality hash functions.  This is

* critical because HashMap uses power-of-two length hash tables, that

* otherwise encounter collisions for hashCodes that do not differ

* in lower bits. Note: Null keys always map to hash 0, thus index 0.

*/

//Null键的散列码总是0

final int hash(Object k) {

int h = 0;

if (useAltHashing) {

if (k instanceof String) {

return sun.misc.Hashing.stringHash32((String) k);

}

h = hashSeed;

}

h ^= k.hashCode();

// This function ensures that hashCodes that differ only by

// constant multiples at each bit position have a bounded

// number of collisions (approximately 8 at default load factor).

h ^= (h >>> 20) ^ (h >>> 12);

return h ^ (h >>> 7) ^ (h >>> 4);

}

/**

* Returns the number of key-value mappings in this map.

*

* @return the number of key-value mappings in this map

*/

public int size() {

return size;

}

/**

* Returns true if this map contains no key-value mappings.

*

* @return true if this map contains no key-value mappings

*/

public boolean isEmpty() {

return size == 0;

}

public V get(Object key) {

if (key == null)

return getForNullKey();

Entry entry = getEntry(key);

return null == entry ? null : entry.getValue();

}

//HashMap底层结构是Entry数组,而数组中每个元素又是一个Entry链表,元素的位置由hash code决定

//对于Null作为k的Entry, hash code始终是0,index也为0,但index为0的位置的链表中还有非Null键的元素,所以要遍历链表

private V getForNullKey() {

for (Entry e = table[0]; e != null; e = e.next) {

if (e.key == null)

return e.value;

}

return null;

}

final Entry getEntry(Object key) {

int hash = (key == null) ? 0 : hash(key);//获取到key的散列码

        //根据散列码找到Entry在数组中存放的位置,再遍历该位置上的Entry链表

        for (Entry e = table[indexFor(hash, table.length)];

e != null;

e = e.next) {

Object k;

if (e.hash == hash &&

((k = e.key) == key || (key != null && key.equals(k))))

return e;

}

return null;

}

/**

* Returns index for hash code h.

*根据hash code返回Entry在数组中的存放位置

*/

//当容量一定是2^n时,h & (length - 1) == h % length,它俩是等价不等效的,位运算效率非常高,

//实际开发中,很多的数值运算以及逻辑判断都可以转换成位运算,但是位运算通常是难以理解的,因为其本身就是给电脑运算的,运算的是二进制,

//而不是给人类运算的,人类运算的是十进制,这也是位运算在普遍的开发者中间不太流行的原因(门槛太高)。

static int indexFor(int h, int length) {

return h & (length-1);

}

//是否包含指定的key

public boolean containsKey(Object key) {

return getEntry(key) != null;

}

/**

* Associates the specified value with the specified key in this map.

* If the map previously contained a mapping for the key, the old

* value is replaced.【相同key,value会被覆盖】

*/

public V put(K key, V value) {

if (key == null)

return putForNullKey(value);

int hash = hash(key);

int i = indexFor(hash, table.length);

//每次put都要遍历table[i],确保不存在才添加

        for (Entry e = table[i]; e != null; e = e.next) {

Object k;

if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

V oldValue = e.value;

e.value = value;

e.recordAccess(this);

return oldValue;

}

}

modCount++;

addEntry(hash, key, value, i);

return null;

}

private V putForNullKey(V value) {

for (Entry e = table[0]; e != null; e = e.next) {

if (e.key == null) {

V oldValue = e.value;

e.value = value;

e.recordAccess(this);

return oldValue;

}

}

modCount++;

addEntry(0, null, value, 0);

return null;

}

/**

下面来看看addEntry方法,参数bucketIndex就是当前元素应该插入到entry数组的下标,先取出放在此位置的entry,然后把当前元素放入该数组中,当前元素的next指向之前取出元素,形成entry链表。(描述的不是很清楚,大概就是把新加入的entry当成头放入到数组当中,然后指向之前的链表),放入之后就去判断当前的size是否达到了threshold极限值,若达到了,将会进行扩容。

*/

void addEntry(int hash, K key, V value, int bucketIndex) {

if ((size >= threshold) && (null != table[bucketIndex])) {

resize(2 * table.length);

hash = (null != key) ? hash(key) : 0;

bucketIndex = indexFor(hash, table.length);

}

createEntry(hash, key, value, bucketIndex);

}

void createEntry(int hash, K key, V value, int bucketIndex) {

Entry e = table[bucketIndex];//将该位置旧的链表取出

        //将要添加的k,v构成entry,作为链表中头节点,再将整个新的链表放到该位置

        table[bucketIndex] = new Entry<>(hash, key, value, e);

size++;

}

/**

* Removes the mapping for the specified key from this map if present.

*/

public V remove(Object key) {

Entry e = removeEntryForKey(key);

return (e == null ? null : e.value);

}

final Entry removeEntryForKey(Object key) {

int hash = (key == null) ? 0 : hash(key);

int i = indexFor(hash, table.length);

Entry prev = table[i];

Entry e = prev;

while (e != null) {//循环

              Entry next = e.next;

Object k;

if (e.hash == hash &&

((k = e.key) == key || (key != null && key.equals(k)))) {

modCount++;

size--;

if (prev == e)

table[i] = next;

else

prev.next = next;

e.recordRemoval(this);

return e;

}

prev = e;

e = next;

}

return e;

}

/**

* Removes all of the mappings from this map.

* The map will be empty after this call returns.

*/

public void clear() {

modCount++;

Entry[] tab = table;

for (int i = 0; i < tab.length; i++)

tab[i] = null;

size = 0;

}

//要重载元素的equals()

public boolean containsValue(Object value) {

if (value == null)

return containsNullValue();

Entry[] tab = table;

for (int i = 0; i < tab.length ; i++)

for (Entry e = tab[i] ; e != null ; e = e.next)

if (value.equals(e.value))

return true;

return false;

}

static class Entry implements Map.Entry {

final K key;

V value;

Entry next;

int hash;

/**

* Creates new entry.

*/

Entry(int h, K k, V v, Entry n) {

value = v;

next = n;

key = k;

hash = h;

}

private abstract class HashIterator implements Iterator {

Entry next;        // next entry to return

int expectedModCount;  // For fast-fail

int index;              // current slot

Entry current;    // current entry

//KeySet和 Values都是内部类

      public Set keySet() {

Set ks = keySet;

return (ks != null ? ks : (keySet = new KeySet()));

}

private final class KeySet extends AbstractSet {

public Iterator iterator() {

return newKeyIterator();

}

public int size() {

return size;

}

public boolean contains(Object o) {

return containsKey(o);

}

public boolean remove(Object o) {

return HashMap.this.removeEntryForKey(o) != null;

}

public void clear() {

HashMap.this.clear();

}

}

public Collection values() {

Collection vs = values;

return (vs != null ? vs : (values = new Values()));

}

private final class Values extends AbstractCollection {

public Iterator iterator() {

return newValueIterator();

}

public int size() {

return size;

}

public boolean contains(Object o) {

return containsValue(o);

}

public void clear() {

HashMap.this.clear();

}

}

//EntrySet也是内部类

  public Set> entrySet() {

return entrySet0();

}

private Set> entrySet0() {

Set> es = entrySet;

return es != null ? es : (entrySet = new EntrySet());

}

private final class EntrySet extends AbstractSet> {

public Iterator> iterator() {

return newEntryIterator();

}

public boolean contains(Object o) {

if (!(o instanceof Map.Entry))

return false;

Map.Entry e = (Map.Entry) o;

Entry candidate = getEntry(e.getKey());

return candidate != null && candidate.equals(e);

}

public boolean remove(Object o) {

return removeMapping(o) != null;

}

public int size() {

return size;

}

public void clear() {

HashMap.this.clear();

}

}

相关文章

网友评论

      本文标题:HashMap源码分析

      本文链接:https://www.haomeiwen.com/subject/xunljftx.html