5.1、对于HashMap需要掌握以下几点

  • Map的创建:HashMap()
  • 往Map中添加键值对:即put(Object key, Object value)方法
  • 获取Map中的单个对象:即get(Object key)方法
  • 删除Map中的对象:即remove(Object key)方法
  • 判断对象是否存在于Map中:containsKey(Object key)
  • 遍历Map中的对象:即keySet(),在实际中更常用的是增强型的for循环去做遍历
  • Map中对象的排序:主要取决于所采取的排序算法

 

5.2、构建HashMap

 源代码:

一些属性:

    static final int DEFAULT_INITIAL_CAPACITY = 16;    // 默认的初始化容量(必须是2的多少次方)
    static final int MAXIMUM_CAPACITY = 1 << 30;    // 最大指定容量为2的30次方
    static final float DEFAULT_LOAD_FACTOR = 0.75f;    // 默认的加载因子(用于resize)

    transient Entry[] table;// Entry数组(数组容量必须是2的多少次方,若有必要会扩容resize)--这就是HashMap的底层数据结构

    transient int size;        // 该map中存放的key-value对个数,该个数决定了数组的扩容(而非table中的所占用的桶的个数来决定是否扩容)
    // 扩容resize的条件:eg.capacity=16,load_factor=0.75,threshold=capacity*load_factor=12,即当该map中存放的key-value对个数size>=12时,就resize)
    int threshold;            
    final float loadFactor;    // 负载因子(用于resize)

    transient volatile int modCount;// 标志位,用于标识并发问题,主要用于迭代的快速失败(在迭代过程中,如果发生了put(添加而不是更新的时候)、remove操作,该值发生变化,快速失败)

注意:

  • map中存放的key-value对个数size,该个数决定了数组的扩容(size>=threshold时,扩容),而非table中的所占用的桶的个数来决定是否扩容
  • 标志位modCount采用volatile实现该变量的线程可见性(之后会在"Java并发"章节中去讲)
  • 数组中的桶,指的就是table[i]
  • threshold默认为0.75,这是综合时间和空间的利用率来考虑的,通常不要变,如果该值过大,可能会造成链表太长,导致get、put等操作缓慢;如果太小,空间利用率不足。

无参构造器(也是当下最常用的构造器)

    /**
     * 初始化一个负载因子、resize条件和Entry数组
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;// 负载因子:0.75
        threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);//当该map中存放的key-value对个数size>=12时,就resize
        table = new Entry[DEFAULT_INITIAL_CAPACITY];// 设置Entry数组容量为16
        init();
    }

注意:

  • init()为空方法

对于hashmap而言,还有两个比较常用的构造器,一个双参,一个单参。

    /**
     * 指定初始容量和负载因子
     */
    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))//loadFactor<0或者不是一个值
            throw new IllegalArgumentException("Illegal load factor:"+loadFactor);

        /*
         * 下边的逻辑是找一个2的几次方的数,该数刚刚大于initialCapacity
         * eg.当指定initialCapacity为17,capacity就是32(2的五次方),而2的四次方(16)正好小于17
         */
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;// capacity = capacity<<1

        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        table = new Entry[capacity];
        init();
    }

    /**
     * 指定初始容量
     */
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);//调用上边的双参构造器
    }
View Code

相关文章: