HashMap笔记
来源:互联网 发布:淘宝保证金怎么解冻 编辑:程序博客网 时间:2024/05/21 09:10
HashMap是Map的主要实现类,Map可查看点击打开链接
HashMap中保存着key-value的映射关系对元素,简称为mapping,即是HashMap.Entry。
mapping的key是不能重复的,即HashMap里不能包含两个相同的key,而mapping的value是可以重复相同,即不同的key可以映射相同的value。key和value的规则是由Map接口定义的,而这个规则的实现由HashMap的父类AbstractMap来实现限制的,在AbstractMap中,key是储存在Set里,而value储存在Collection里的。
判断两个key是否相同,是通过key的hashCode()和equals()两个方法来判断的,比如String类型的key,那么可以通过String类的hashCode()和equals()两个方法进行相关计算来判断key是否相同的,详情看完本文便清楚。
AbstractMap.java:
/** * Each of these fields are initialized to contain an instance of the * appropriate view the first time this view is requested. The views are * stateless, so there's no reason to create more than one of each. */ transient volatile Set<K> keySet = null; transient volatile Collection<V> values = null;
HashMap是用数组和链表来储存mapping的,根据mapping的key的hashcode,生成mapping的hash属性的值,再根据hash值和(当前数组的长度减1)进行与运算(&)
生成索引index,mapping就存放在table[index]。
首先看看HashMap中储存mapping的数据结构图
在HashMap的保存、查找、删除操作中,有两个方法是非常重要的,就是生成mapping的hash值,生成mapping在数组中的索引位置index。
生成hash值:
/** * Applies a supplemental hash function to a given hashCode, 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. */ static int hash(int h) { // 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); }其中,参数h主要是使用mapping的key.hashCode();
生成索引位置index:
/** * Returns index for hash code h. */ static int indexFor(int h, int length) { return h & (length-1); }
一、HashMap保存mapping
当key为null,则Entry的hash值为0,Entry储存的index为0;
如果key不为null,则用key的hash值进行位移运算生成hash值(HashMap.hash方法),再用hash值和table.length进行与运算生成index(HashMap.indexFor方法),
如果table[index]存在key值的Entry元素,则设置新的value值,(那么如何判断table[index]已经存在key值呢?可以往下看 二、HashMap查找mapping。);
否则,生成Entryentry1, table[index]= entry1,原来储存在table[index]的元素则作为entry1的next属性,当size++ >= threshold时,
扩大数组容量为原来的2倍(如oldCapacity = 16,则newCapacity=32,),且threshold设置为newCapacity *loadFactory;
但如果oldCapacity已经达到最大值(Integer.MAXIMUX_VALUE),则不再扩容,且将threshold设为Integer.MAXIMUX_VALUE。
<span style="font-size:12px;">/** * 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. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); 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; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }/** * Adds a new entry with the specified key, value and hash code to * the specified bucket. It is the responsibility of this * method to resize the table if appropriate. * * Subclass overrides this to alter the behavior of put method. */ void addEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K,V>(hash, key, value, e); if (size++ >= threshold) resize(2 * table.length); }</span>
二、HashMap查找mapping
HashMap不用循环遍历数组查找,只需根据key的hashCode生成hash值,
再根据hash值和数组的长度生成索引index,便可找到mapping的位置,table[index]就存放着要找的mapping,不过table[index]储存着一条链表,链表可能有多个Entry对象链接在一起,HashMap是根据Entry.key的hashCode()和equals()来判断是否和要找的mapping的key是否同等,同等则是要的mapping,否则不然,所以平时我们自己定义的类实现hashCode()和equals()两个方法是非常有必要的,比如自己定义的类需要储存在Map对象里,那么hashCode()和equals()就显得比较重要,等等其它情况也可能用到的。
HashMap的V get(Object k);
/** * Returns the value to which the specified key is mapped, * or {@code null} if this map contains no mapping for the key. * * <p>More formally, if this map contains a mapping from a key * {@code k} to a value {@code v} such that {@code (key==null ? k==null : * key.equals(k))}, then this method returns {@code v}; otherwise * it returns {@code null}. (There can be at most one such mapping.) * * <p>A return value of {@code null} does not <i>necessarily</i> * indicate that the map contains no mapping for the key; it's also * possible that the map explicitly maps the key to {@code null}. * The {@link #containsKey containsKey} operation may be used to * distinguish these two cases. * * @see #put(Object, Object) */ public V get(Object key) { if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; }
三、HashMap删除mapping
HashMap通过mapping生成hash值,找到mapping,即为table[index]。(如何找到mapping,可以参考二、HashMap查找mapping)
在table[index]指向的链表中找到同等key的mapping,并且从链表中删除该mapping,且size值减一
/** * Removes the mapping for the specified key from this map if present. * * @param key key whose mapping is to be removed from the map * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ public V remove(Object key) { Entry<K,V> e = removeEntryForKey(key); return (e == null ? null : e.value); } /** * Removes and returns the entry associated with the specified key * in the HashMap. Returns null if the HashMap contains no mapping * for this key. */ final Entry<K,V> removeEntryForKey(Object key) { int hash = (key == null) ? 0 : hash(key.hashCode()); int i = indexFor(hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> e = prev; 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--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; } return e; }
补充:HashMap和Hashtable是比较相似的,主要是HashMap的方法是不加锁的,而Hashtable是加锁的。
- HashMap笔记
- HashMap笔记
- HashMap笔记
- [笔记]HashMap
- HashMap笔记
- HashMap 源码笔记
- Java学习笔记-HashMap
- HashMap关键原理笔记
- HashMap源码学习笔记
- HashMap源码阅读笔记
- HashMap源码分析笔记
- HashMap学习笔记
- 面试笔记----------HashMap排序
- HashMap学习笔记
- HashMap学习笔记
- HashMap学习笔记
- HashMap学习笔记
- HashMap源码学习笔记
- 水题水坑,虐心到哭
- Oracle job procedure 存储过程定时任务
- SSL链接相关文章
- 【MATLAB】一维搜索之抛物线法
- jQuery-添加、移出事件
- HashMap笔记
- 要熟记
- Effective Java - 第45条:将局部变量的作用域最小化
- 【MATLAB】一维搜索之平分法
- ssh的用法,关于公钥和私钥
- 【MATLAB】一维搜索之成功失败法
- 解决UITableView中Cell重用机制导致内容出错的方法总结
- TCP中send和recv函数针对的是字符(无边界), 而不是字符串(以'\0'作为边界)
- 【MATLAB】二维搜索之阻尼牛顿法