HashMap源码解析
来源:互联网 发布:无基础学编程看什么书 编辑:程序博客网 时间:2024/05/22 17:47
疑问:.Set和Map之间是怎么匹配的,Map中包含Set
一、定义
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
扩展自AbstractMap,
重新实现Map
实现Cloneable,浅克隆
实现Serializable,序列化
Map定义了一些基本的map方法
public interface Map<K,V> { //返回的set与原HashMap有映射关系,所以修改其中任意一个,另外一个也会改变 Set<K> keySet(); //返回的Collection与原HashMap有映射关系,所以修改其中任意一个,另外一个也会改变 Collection<V> values(); //返回的set与原HashMap有映射关系,所以修改其中任意一个,另外一个也会改变 Set<Map.Entry<K, V>> entrySet(); //接口中定义接口 interface Entry<K,V> { K getKey(); V getValue(); V setValue(V value); boolean equals(Object o); int hashCode(); }}
AbstractMap抽象类
/** * 包括两个内部类,这两个类都是key-value类Entry的简单实现 * 其中SimpleEntry可以用于setValue值,SimpleImmutableEntry不可以 * 这两个都是为子类准备的 * * 当用到Map类中的内部类Entry时,有的是调用Map.Entry,有的直接调用Entry * * containsValue,get、remove等方法都是通过entrySet()的迭代器为基础进行操作的 * * equals比较时不会比较排列顺序,在有顺序的子类Map中应该会覆盖此类方法 * * 发现泛型中调用内部类Entry,直接调用或者通过Map.Entry都可以 */public abstract class AbstractMap<K,V> implements Map<K,V> { /** * Sole constructor. (For invocation by subclass constructors, typically * implicit.) */ protected AbstractMap() { } // Query Operations //返回entrySet().size() public int size() { return entrySet().size(); } //此实现所需的时间与映射的大小呈线性关系 public boolean containsValue(Object value) { Iterator<Entry<K,V>> i = entrySet().iterator(); if (value==null) { while (i.hasNext()) { Entry<K,V> e = i.next(); if (e.getValue()==null) return true; } } else { while (i.hasNext()) { Entry<K,V> e = i.next(); //通过equals来比较 if (value.equals(e.getValue())) return true; } } return false; } public V put(K key, V value) { throw new UnsupportedOperationException(); } public void putAll(Map<? extends K, ? extends V> m) { for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } // transient 自定义序列化 // volatile 线程相关,保证原子性 // 查看HashMap后,明白这两变量是用来存放keySet,values值 transient volatile Set<K> keySet = null; transient volatile Collection<V> values = null; /** * 返回的set与Map有映射关系,修改其中一方对另一方都会造成影响 * 返回的是一个set<key>只有key值,但set.iterator利用的却是set<key,value>,这样做的原因? * * 实际存储是以映射关系存储的,以Entry来封装, * * 方法中内部类size,contains等方法交给外部类处理 */ public Set<K> keySet() { if (keySet == null) { keySet = new AbstractSet<K>() { public Iterator<K> iterator() { return new Iterator<K>() { //private 修饰 private Iterator<Entry<K,V>> i = entrySet().iterator(); public boolean hasNext() { return i.hasNext(); } public K next() { return i.next().getKey(); } public void remove() { i.remove(); } }; } //调用外部类的方法 public int size() { return AbstractMap.this.size(); } public boolean isEmpty() { return AbstractMap.this.isEmpty(); } public void clear() { AbstractMap.this.clear(); } public boolean contains(Object k) { return AbstractMap.this.containsKey(k); } }; } return keySet; } public abstract Set<Entry<K,V>> entrySet(); //没有比较元素之间的顺序 public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Map)) return false; Map<K,V> m = (Map<K,V>) o; if (m.size() != size()) return false; try { Iterator<Entry<K,V>> i = entrySet().iterator(); while (i.hasNext()) { //有的地方用Map.Entry有的地方用Entry Entry<K,V> e = i.next(); K key = e.getKey(); V value = e.getValue(); //没有比较元素之间的顺序 if (value == null) { if (!(m.get(key)==null && m.containsKey(key))) return false; } else { if (!value.equals(m.get(key))) return false; } } //这个两个catch?? //classCast应该是o转换时校验?NullPointer呢?在调用方法中没体现啊 } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } return true; } /** * 浅克隆都算不上,子类应该会覆盖此方法 * 翻到HashMap中查看,果然如此 */ protected Object clone() throws CloneNotSupportedException { AbstractMap<K,V> result = (AbstractMap<K,V>)super.clone(); result.keySet = null; result.values = null; return result; } public static class SimpleEntry<K,V> implements Entry<K,V>, java.io.Serializable {} public static class SimpleImmutableEntry<K,V> implements Entry<K,V>, java.io.Serializable {}
二、底层原理
采用线性哈希、链表散列。
每个key和value的映射通过Entry类来体现。
/** * Entry作为单向链表的元素,多个链表组成HashMap的存储数据结构 */ static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next;//指向next Entry int hash;//哈希值 Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; } public final K getKey() { return key; } public final V getValue() { return value; } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } //key和value都相等时,equals返回true public final boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; Object k1 = getKey(); Object k2 = e.getKey(); if (k1 == k2 || (k1 != null && k1.equals(k2))) { Object v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } return false; } public final int hashCode() { return (key==null ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode()); } public final String toString() { return getKey() + "=" + getValue(); } /** * 执行put操作后执行此方法,但方法中并没哟具体操作 * 为子类保留的方法 */ void recordAccess(HashMap<K,V> m) { } /** * 执行remove操作后执行此方法,但方法中并没哟具体操作. * 为子类保留的方法 */ void recordRemoval(HashMap<K,V> m) { } }
三、构造器,基本field
/** * 默认长度16,长度应为2的幕,有利于提高效率,在哪提高效率了呢, */ static final int DEFAULT_INITIAL_CAPACITY = 16; static final int MAXIMUM_CAPACITY = 1 << 30; /** * 默认负载因子 */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * 存放数据的载体,transient * 每个Entry存放的都是链表中的第一个first元素 */ transient Entry<K,V>[] table; transient int size; /** * 下次扩展临界值,阈值 */ int threshold; /** * 负载因子 */ final float loadFactor; /** * 迭代器防止并发错误 */ transient int modCount; 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的幕 int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; this.loadFactor = loadFactor; //阈值取两者中较小的,出现MAXIMUM_CAPACITY+1,是担心负载因子过大 threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); table = new Entry[capacity]; useAltHashing = sun.misc.VM.isBooted() && (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD); init(); } public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } public HashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); } /** * 容量最小为16,负载因子位默认值 */ public HashMap(Map<? extends K, ? extends V> m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); putAllForCreate(m); } // internal utilities /** * 为子类留的钩子 */ void init() { }
四,put,remove,get
先来看几个基本的方法
/** * 获得k的hash值 */ 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); } /** * 通过hash,length获得index值 */ static int indexFor(int h, int length) { return h & (length-1); } /** * 扩充容量必须为2的次方 * 容量已经为最大值时,设置阈值为maxIntger,以容纳更多Entry */ void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } Entry[] newTable = new Entry[newCapacity]; boolean oldAltHashing = useAltHashing; useAltHashing |= sun.misc.VM.isBooted() && (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD); boolean rehash = oldAltHashing ^ useAltHashing; //将Entrys转移到newTable中 transfer(newTable, rehash); table = newTable; //改变阈值 threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); } /** * 将Entrys转移到newTable中 */ void transfer(Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; //迭代器迭代期间update元素不会抛出异常 for (Entry<K,V> e : table) { while(null != e) { //保留next Entry<K,V> next = e.next; if (rehash) { e.hash = null == e.key ? 0 : hash(e.key); } //为每个Entry获取新的index int i = indexFor(e.hash, newCapacity); //e插入index为i的链表头 e.next = newTable[i]; newTable[i] = e; e = next; } } }
indexFor的计算方法十分精妙,
&运算:两个操作数中位都为1,结果才为1,否则结果为0.
length为2的N次幕,二进制为1000**00,length-1,二进制为111**11。
当h<=length-1时,h & (length-1) = h;
当h>length-1时,大于length-1的高位全部&运算为0,低位再进行&运算(比如h=101011,length-1=1111,h&(length-1) = 1011 & 1111),即h & (length-1) = h % (length-1)
hash()方法中的计算公式,有兴趣的可以参考http://www.iteye.com/topic/709945
public V get(Object key) { if (key == null) return getForNullKey(); Entry<K,V> entry = getEntry(key); return null == entry ? null : entry.getValue(); } private V getForNullKey() { //null key的hash,index一定为0 for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) return e.value; } return null; } final Entry<K,V> getEntry(Object key) { //计算其hash int hash = (key == null) ? 0 : hash(key); //求出index值,获取子链表中的第一个Entry,作为循环的初始变量 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; //此处判断key和e.key是否相等时,用了((k = e.key) == key || key.equals(k)) //很好理解,比如key是String类型(String覆盖equals方法),e.key与key的内存地址不同,但equsls方法返回true //hashMap比较两个对象时,==和equals有一个相等,两个对象即相等 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null; } public V put(K key, V value) { if (key == null) return putForNullKey(value); //获取hash值 int hash = hash(key); //获取index值 int i = indexFor(hash, table.length); //通过子链表的第一个Rntry向后遍历 for (Entry<K,V> 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; //执行recordAccess e.recordAccess(this); return oldValue; } } modCount++; //链表后添加元素 addEntry(hash, key, value, i); return null; } /** * Offloaded version of put for null keys */ private V putForNullKey(V value) { for (Entry<K,V> 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; } /** * This method is used instead of put by constructors and * pseudoconstructors (clone, readObject). It does not resize the table, * check for comodification, etc. It calls createEntry rather than * addEntry. */ 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<K,V> 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); } private void putAllForCreate(Map<? extends K, ? extends V> m) { for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) putForCreate(e.getKey(), e.getValue()); } public void putAll(Map<? extends K, ? extends V> m) { int numKeysToBeAdded = m.size(); if (numKeysToBeAdded == 0) return; /* * Expand the map if the map if the number of mappings to be added * is greater than or equal to threshold. This is conservative; the * obvious condition is (m.size() + size) >= threshold, but this * condition could result in a map with twice the appropriate capacity, * if the keys to be added overlap with the keys already in this map. * By using the conservative calculation, we subject ourself * to at most one extra resize. * 英文解释的很好啦 */ if (numKeysToBeAdded > threshold) { int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1); if (targetCapacity > MAXIMUM_CAPACITY) targetCapacity = MAXIMUM_CAPACITY; int newCapacity = table.length; while (newCapacity < targetCapacity) newCapacity <<= 1; if (newCapacity > table.length) resize(newCapacity); } for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } public V remove(Object key) { Entry<K,V> e = removeEntryForKey(key); return (e == null ? null : e.value); } /** * 删除元素后,没有进行容量更新操作 */ final Entry<K,V> removeEntryForKey(Object key) { int hash = (key == null) ? 0 : hash(key); int i = indexFor(hash, table.length); //定义前一个链表元素 Entry<K,V> prev = table[i]; //定义当前链表元素,初始时间prev与e为同一个值 Entry<K,V> e = prev; //从first元素开始,遍历链表,找到key相等的元素后执行删除操作 while (e != null) { Entry<K,V> next = e.next; Object k; //遍历链表 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { modCount++; size--; //prev=e时,为first元素,直接更新table[inded] if (prev == e) table[i] = next; else prev.next = next; //remove后执行recordRemoval e.recordRemoval(this); return e; } //更新 prev prev = e; //更新e e = next; } return e; } void addEntry(int hash, K key, V value, int bucketIndex) { //先判断,是否进行expend容量操作 if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); //expand容量后重新计算 hash = (null != key) ? hash(key) : 0; //expand容量后重新计算index值 bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); } /** * Like addEntry except that this version is used when creating entries * as part of Map construction or "pseudo-construction" (cloning, * deserialization). This version needn't worry about resizing the table. * * Subclass overrides this to alter the behavior of HashMap(Map), * clone, and readObject. */ void createEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; //单链表表头插入 table[bucketIndex] = new Entry<>(hash, key, value, e); //size自增 size++; }
四,keySet,values,entrySet
HashMap提供了三个迭代器,分别用于生成keySet,values,Entry
private abstract class HashIterator<E> implements Iterator<E> { Entry<K,V> next; // next entry to return int expectedModCount; // For fast-fail int index; // current slot Entry<K,V> current; // current entry HashIterator() { expectedModCount = modCount; if (size > 0) { // advance to first entry Entry[] t = table; //将next指向第一个不为null的链表 //current没有赋值哦 while (index < t.length && (next = t[index++]) == null) ; } } public final boolean hasNext() { return next != null; } final Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); Entry<K,V> e = next; if (e == null) throw new NoSuchElementException(); if ((next = e.next) == null) { Entry[] t = table; while (index < t.length && (next = t[index++]) == null) ; } current = e; return e; } public void remove() { if (current == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); Object k = current.key; current = null; HashMap.this.removeEntryForKey(k); expectedModCount = modCount; } } private final class ValueIterator extends HashIterator<V> { public V next() { return nextEntry().value; } } private final class KeyIterator extends HashIterator<K> { public K next() { return nextEntry().getKey(); } } private final class EntryIterator extends HashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } } // Subclass overrides these to alter behavior of views' iterator() method Iterator<K> newKeyIterator() { return new KeyIterator(); } Iterator<V> newValueIterator() { return new ValueIterator(); } Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); } // Views private transient Set<Map.Entry<K,V>> entrySet = null; /** * 返回的set与hashMap存在映射关系,修改其中一个,另一个也会改变 * keySet在哪里被初始化赋值的呢? * 返回的set不支持add和addAll操作,什么原因 */ public Set<K> keySet() { Set<K> ks = keySet; //尚未赋值为null时,直接赋值并返回 return (ks != null ? ks : (keySet = new KeySet())); } //都是调用HashMap中的方法,很像代理 private final class KeySet extends AbstractSet<K> { public Iterator<K> 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(); } } //同keySet一致 public Collection<V> values() { Collection<V> vs = values; return (vs != null ? vs : (values = new Values())); } //remove方法继承自AbstractCollection,只删除了value,没有删除映射的Entry, //原来AbstractCollection中的remove方法时先获得iterator,然后通过iterator删除, private final class Values extends AbstractCollection<V> { public Iterator<V> iterator() { return newValueIterator(); } public int size() { return size; } public boolean contains(Object o) { return containsValue(o); } public void clear() { HashMap.this.clear(); } } /** * Returns a {@link Set} view of the mappings contained in this map. * The set is backed by the map, so changes to the map are * reflected in the set, and vice-versa. If the map is modified * while an iteration over the set is in progress (except through * the iterator's own <tt>remove</tt> operation, or through the * <tt>setValue</tt> operation on a map entry returned by the * iterator) the results of the iteration are undefined. The set * supports element removal, which removes the corresponding * mapping from the map, via the <tt>Iterator.remove</tt>, * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and * <tt>clear</tt> operations. It does not support the * <tt>add</tt> or <tt>addAll</tt> operations. * * @return a set view of the mappings contained in this map */ public Set<Map.Entry<K,V>> entrySet() { return entrySet0(); } private Set<Map.Entry<K,V>> entrySet0() { Set<Map.Entry<K,V>> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } private final class EntrySet extends AbstractSet<Map.Entry<K,V>> { public Iterator<Map.Entry<K,V>> iterator() { return newEntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K,V> e = (Map.Entry<K,V>) o; Entry<K,V> 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(); } }
五,serialize
private void writeObject(java.io.ObjectOutputStream s) throws IOException { Iterator<Map.Entry<K,V>> i = (size > 0) ? entrySet0().iterator() : null; // Write out the threshold, loadfactor, and any hidden stuff s.defaultWriteObject(); // Write out number of buckets s.writeInt(table.length); // Write out size (number of Mappings) s.writeInt(size); // Write out keys and values (alternating) if (size > 0) { for(Map.Entry<K,V> e : entrySet0()) { //key,value依次写入 s.writeObject(e.getKey()); s.writeObject(e.getValue()); } } } private static final long serialVersionUID = 362498820763181265L; /** * Reconstitute the {@code HashMap} instance from a stream (i.e., * deserialize it). */ private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the threshold (ignored), loadfactor, and any hidden stuff s.defaultReadObject(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new InvalidObjectException("Illegal load factor: " + loadFactor); // set hashSeed (can only happen after VM boot) Holder.UNSAFE.putIntVolatile(this, Holder.HASHSEED_OFFSET, sun.misc.Hashing.randomHashSeed(this)); // Read in number of buckets and allocate the bucket array; s.readInt(); // ignored // Read number of mappings int mappings = s.readInt(); if (mappings < 0) throw new InvalidObjectException("Illegal mappings count: " + mappings); //重新计算容量 int initialCapacity = (int) Math.min( // capacity chosen by number of mappings // and desired load (if >= 0.25) //这样做的原因?? mappings * Math.min(1 / loadFactor, 4.0f), // we have limits... HashMap.MAXIMUM_CAPACITY); int capacity = 1; // find smallest power of two which holds all mappings while (capacity < initialCapacity) { capacity <<= 1; } table = new Entry[capacity]; //重新计算负载因子 threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); useAltHashing = sun.misc.VM.isBooted() && (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD); init(); // Give subclass a chance to do its thing. // Read the keys and values, and put the mappings in the HashMap for (int i=0; i<mappings; i++) { K key = (K) s.readObject(); V value = (V) s.readObject(); putForCreate(key, value); } }
阅读源码时,如果有一定的算法基础就事半功倍。
阅读AbstractMap的其他子类后,相互之间比较区分一下。
- Android源码解析 -- HashMap
- HashMap源码解析
- HashMap源码解析
- HashMap源码解析
- HashMap源码解析
- HashMap源码解析
- HashMap源码解析
- Java HashMap 源码解析
- Java HashMap 源码解析
- Java HashMap 源码解析
- 源码解析HashMap
- Java:HashMap源码解析
- HashMap 源码解析
- HashMap源码解析
- HashMap 源码解析
- Java源码解析-hashmap
- HashMap源码解析
- HashMap源码解析
- Windchill常用命令
- Python 简介
- hdu3591 The trouble of Xiaoqian(多重背包 + 完全背包)
- Java的内存机制
- HDU 2604 Queuing(矩阵快速幂)
- HashMap源码解析
- RxJava 源码走读之Observable.create()和subscribe()
- 降低PNG图片存储大小方法、图片压缩方法
- HTTP协议详解
- 1002
- Scala函数
- PVR图像文件格式初探
- 一元分成 一分,二分,五分的不同分法
- Day1