美文网首页
Java集合框架1HashMap

Java集合框架1HashMap

作者: paulpaullong | 来源:发表于2017-04-05 16:54 被阅读0次

    HashMap定义

    • 1 本文以jdk7为准进行说明
    package java.util;
    import java.io.*;
    public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Serializable{
            static final Entry<?,?>[] EMPTY_TABLE = {}; // 空桶
            transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE; // 桶容器
            static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;  // 默认容量
            static final float DEFAULT_LOAD_FACTOR = 0.75f;  // 默认负载因子
           
            transient int modCount;
            // 其他成员变量和方法。。。
    }
    
    • 2 主要成员属性
      1)table属性是一个数组,数组的元素是Entry<?, ?>,Entry保存的是key-value键值对。
      2)DEFAULT_INITIAL_CAPACITY属性是默认容量,大小为16,是HashMap的默认size值。
      3)DEFAULT_LOAD_FACTOR 属性是默认负载因子,当HashMap的size超过容量 X 负载因子时,HashMap经被扩容。
      4)modCount成员属性用来实现“fast-fail”机制(也就是快速失败)。即在并发集合中,其进行迭代操作时,若有其他线程对其结构性的修改,这是迭代器会立马感知到,并且立刻抛出ConcurrentModificationException异常。

    HashMap的数据结构

    • 1 当初始化 HashMap 时,系统会创建一个长度为16的Entry数组,数组里存储的元素Entry也被称为“桶(bucket)”,每个 bucket 都有其指定索引,对于HashMap及其子类而言,它们采用Hash算法来计算元素的索引值,即hash(key.hashCode())。
    • 2 如果同时有两个以上的Entry的key.hashCode()一样,那么通过Hash算法计算出来的hash值也一样,这样就发生了hash碰撞,会导致该索引位置同时有多个Entry元素;
    • 3 但是HashMap 的每个“桶”只能存储一个元素,这种情况下,会通过Entry里的next引用,将这几个Entry组成一个链表(Java8中,当该链表长度大于8时,会将链表转变成红黑树,来提高查询速度)。

    HashMap源码分析

    • 1 put方法详解
    // put方法
    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
    
        if (key == null) {
            return putForNullKey(value);
        }          
    
        int hash = hash(key);
        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;
    }
    // addEntry方法
    public 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);
    }
    

    1)判断table是否为空,如果为空则扩充table,其中包括确保table的大小为2的整数倍。
    2)如果key值为null,则特殊处理,调用putForNullKey(V value),hash值为0,存入table中,返回。
    3)如果key值不为null,则计算key的hash值;
    4)计算key在table中的索引index;
    5)通过索引找到table[index]位置上的Entry“桶”,如果该桶为null,则直接进行增加操作;
    6)如果该桶不为null,对其进行遍历,如果发现链表中有Entry的的key一致(hash和equals都一致),则用新值(value)替换旧值(oldValue),并保证key的唯一性;如果Entry“桶”内的所有key没有一个和传进来的key一致的,则进行增加操作。
    7)增加操作前先判断是否需要扩容,然后将新加的Entry放到table数组中,之前在table数组中的Entry则被新加的Entry的next引用所指向。例如插入的顺序,原先table[index]的Entry链表是 old1->old2->old3,插入新值之后为new1->old1->old2->old3。

    • 2 get方法详解
    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;   
    }   
    

    1)判断key值是否为null,如果是则特殊处理(在table[0]的链表中寻找)
    2)否则计算hash值,进而获得table中的index值
    3)在table[index]的链表中寻找,根据hash值和equals()方法获得相应的value。
    4)如果找不到,最后返回null。

    使用自定义的类的对象作为键

    • 1 重写自定义类的equals()和hashCode()方法
    • 2 当对象插入到Map中之后不能再被改变

    HashMap中的序列化问题

    • 1 HashMap实现了Serializable接口,但是对table的定义(transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;)却是transient(不再是对象序列化的一部分)的。
    • 2 HashMap的put与get基于hashCode的实现,而hashCode是native方法,对每一个不同的java环境来说,同一个key所计算的hashCode是不相同的,所以反序列化后table的index会发生变化,无法还原
    • 3 HashMap默认size到达阈值threshold之后进行扩容,很明显HashMap不可能保证每一个bullet都有数据,很多都为null,如果对这部分数据进行序列化则造成不必要的资源浪费。

    相关文章

      网友评论

          本文标题:Java集合框架1HashMap

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