HashMap笔记

来源:互联网 发布:淘宝保证金怎么解冻 编辑:程序博客网 时间:2024/05/21 09:10

      HashMap是Map的主要实现类,Map可查看点击打开链接

      HashMap中保存着key-value的映射关系对元素,简称为mapping,即是HashMap.Entry

      mappingkey是不能重复的,即HashMap里不能包含两个相同的key,而mapping的value是可以重复相同,即不同的key可以映射相同的valuekeyvalue的规则是由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的,根据mappingkeyhashcode生成mappinghash属性的值,再根据hash值和(当前数组的长度减1)进行与运算(&

生成索引indexmapping就存放在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

keynull,Entryhash值为0Entry储存的index0;

如果key不为null,则用keyhash值进行位移运算生成hash值(HashMap.hash方法),再用hash值和table.length进行与运算生成indexHashMap.indexFor方法)

如果table[index]存在key值的Entry元素,则设置新的value值,(那么如何判断table[index]已经存在key值呢?可以往下看 二、HashMap查找mapping。);

否则,生成Entryentry1 table[index]= entry1,原来储存在table[index]的元素则作为entry1next属性,当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不用循环遍历数组查找,只需根据keyhashCode生成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]指向链表中找到同等keymapping,并且从链表中删除该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是加锁的。
0 0
原创粉丝点击