jdk1.8

arraylist源码分析--jdk1.8
linkedlist源码分析--jdk1.8
hashmap源码分析--jdk1.8

hashmap概述

  1. hashmap是可以动态扩容的数组,基于数组、链表、红黑树实现的集合。
  2. hashmap支持键值对取值、克隆、序列化,元素无序,key不可重复value可重复,都可为null。
  3. hashmap初始默认长度16,超出扩容2倍,填充因子0.75f。
  4.hashmap当链表的长度大于8的且数组大小大于64时,链表结构转变为红黑树结构。

hashmap数据结构

  数据结构是集合的精华所在,数据结构往往也限制了集合的作用和侧重点,了解各种数据结构是我们分析源码的必经之路。
  hashmap的数据结构如下:数组+链表+红黑树
HashMap源码分析--jdk1.8

hashmap源码分析

/*
 * 用数组+链表+红黑树实现的集合,支持键值对查找
 */
public class hashmap<k,v> extends abstractmap<k,v>
    implements map<k,v>, cloneable, serializable {

    private static final long serialversionuid = 362498820763181265l;

    /**
     * 默认初始容量-必须是2的幂
     * 1*2的4次方    默认长度16
     */
    static final int default_initial_capacity = 1 << 4;

    /**
     * 最大容量 
     * 1*2的30次方   最大容量1073741824
     */
    static final int maximum_capacity = 1 << 30;

    /**
     *  默认的填充因子  0.75
     *  负载因子0.75是对空间和时间效率的一个平衡选择,建议大家不要修改,除非在时间和空间比较特殊的情况下,
     *  如果内存空间很多而又对时间效率要求很高,可以降低负载因子load factor的值;
     *  相反,如果内存空间紧张而对时间效率要求不高,可以增加负载因子loadfactor的值,这个值可以大于1
     */
    static final float default_load_factor = 0.75f;

    /**
     * 当桶(bucket)上的节点数大于这个值时会转成红黑树
     */
    static final int treeify_threshold = 8;

    /**
     * 当桶(bucket)上的节点数小于这个值时树转链表
     */
    static final int untreeify_threshold = 6;

    /**
     * 桶中结构转化为红黑树对应的table的最小大小
     */
    static final int min_treeify_capacity = 64;

    /**
     * node是单向链表,它实现了map.entry接口
     */
    static class node<k,v> implements map.entry<k,v> {
        final int hash;
        final k key;
        v value;
        node<k,v> next;
      //构造函数hash值 键 值 下一个节点
        node(int hash, k key, v value, node<k,v> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final k getkey()        { return key; }
        public final v getvalue()      { return value; }
        public final string tostring() { return key + "=" + value; }

        public final int hashcode() {
            return objects.hashcode(key) ^ objects.hashcode(value);
        }

        // 实现接口定义的方法,且该方法不可被重写
        // 设值,返回旧值
        public final v setvalue(v newvalue) {
            v oldvalue = value;
            value = newvalue;
            return oldvalue;
        }
      //构造函数hash值 键 值 下一个节点
      /*
       * 重写父类object的equals方法,且该方法不可被自己的子类再重写
       * 判断相等的依据是,只要是map.entry的一个实例,并且键键、值值都相等就返回true
       */
        public final boolean equals(object o) {
            if (o == this)
                return true;
            if (o instanceof map.entry) {
                map.entry<?,?> e = (map.entry<?,?>)o;
                if (objects.equals(key, e.getkey()) &&
                    objects.equals(value, e.getvalue()))
                    return true;
            }
            return false;
        }
    }

hashmap继承和实现分析

HashMap源码分析--jdk1.8
   hashmap extends abstractmap
   abstractmap extends object
  java中所有类都继承object,所以hashmap的继承结构如上图。
   1. abstractmap是一个抽象类,实现了map<k,v>接口,map<k,v>定义了一些map(k,v)键值对通用方法,而abstractmap抽象类中可以有抽象方法,还可以有具体的实现方法,abstractmap实现接口中一些通用的方法,实现了基础的/get/remove/containskey/containsvalue/keyset方法,hashmap再继承abstractmap,拿到通用基础的方法,然后自己在实现一些自己特有的方法,这样的好处是:让代码更简洁,继承结构最底层的类中通用的方法,减少重复代码,从上往下,从抽象到具体,越来越丰富,可复用。
   2.hashmap实现了map<k,v>、cloneable、serializable接口
     1)map<k,v>接口,定义了map键值对通用的方法,1.8中为了加强接口的能力,使得接口可以存在具体的方法,前提是方法需要被default或static关键字所修饰,map中实现了一些通用方法实现,使接口更加抽象。
     2)cloneable接口,可以使用object.clone()方法。
     3)serializable接口,序列化接口,表明该类可以被序列化,什么是序列化?简单的说,就是能够从类变成字节流传输,反序列化,就是从字节流变成原来的类

hashmap核心方法分析

1. put方法(10种实现)--增/修改   

HashMap源码分析--jdk1.8
HashMap源码分析--jdk1.8
HashMap源码分析--jdk1.8
HashMap源码分析--jdk1.8

     1)v put(k key, v value);//map添加元素

/**
 * 新增元素
 */
public v put(k key, v value) {
    return putval(hash(key), key, value, false, true);
}
 /**
 * implements map.put and related methods
 * @param hash hash for key
 * @param key the key
 * @param value the value to put
 * @param onlyifabsent if true, don't change existing value
 * onlyifabsent默认传false,覆盖更改现有值
 * onlyifabsent传true,不覆盖更改现有值
 * @param evict if false, the table is in creation mode.
 * @return previous value, or null if none
 */
final v putval(int hash, k key, v value, boolean onlyifabsent,
               boolean evict) {
    node<k,v>[] tab; node<k,v> p; int n, i;
    //如果table为空 或者 长度为0
    if ((tab = table) == null || (n = tab.length) == 0)
        //扩容
        n = (tab = resize()).length;
    //计算index,并对null做处理 
    // (n - 1) & hash 确定元素存放在哪个桶中,桶为空,新生成结点放入桶中(此时,这个结点是放在数组中)
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newnode(hash, key, value, null);
    // 桶中已经存在元素
    else {
        node<k,v> e; k k;
        //如果key存在 直接覆盖 value
        // 比较桶中第一个元素(数组中的结点)的hash值相等,key相等
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            // 将第一个元素赋值给e,用e来记录
            e = p;
        //如果table[i]是红黑树 直接在红黑树中插入
        // hash值不相等,即key不相等;为红黑树结点
        else if (p instanceof treenode)
            e = ((treenode<k,v>)p).puttreeval(this, tab, hash, key, value);
        //如果是链表  则遍历链表
        else {
            // 在链表最末插入结点
            for (int bincount = 0; ; ++bincount) {
                // 到达链表的尾部
                if ((e = p.next) == null) {
                     // 在尾部插入新结点
                    p.next = newnode(hash, key, value, null);
                    // 结点数量达到阈值,转化为红黑树
                    if (bincount >= treeify_threshold - 1) // -1 for 1st
                        treeifybin(tab, hash);
                    // 跳出循环
                    break;
                }
                // 判断链表中结点的key值与插入的元素的key值是否相等
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    // 相等,跳出循环
                    break;
                // 用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表
                p = e;
            }
        }
        // 表示在桶中找到key值、hash值与插入元素相等的结点
        if (e != null) { // existing mapping for key
            // 记录e的value
            v oldvalue = e.value;
            // onlyifabsent为false或者旧值为null
            if (!onlyifabsent || oldvalue == null)
                //用新值替换旧值
                e.value = value;
            // 访问后回调
            afternodeaccess(e);
            // 返回旧值
            return oldvalue;
        }
    }
    // 结构性修改
    ++modcount;
    // 实际大小大于阈值则扩容
    if (++size > threshold)
        resize();
    // 插入后回调,用来回调移除最早放入map的对象(linkedhashmap中实现了,hashmap中为空实现)
    afternodeinsertion(evict);
    return null;
}

     2)putall(map<? extends k, ? extends v> m);//添加map全部元素

/**
 * 添加map全部元素
 */
public void putall(map<? extends k, ? extends v> m) {
    putmapentries(m, true);
}
 /**
 * implements map.putall and map constructor
 * @param m the map
 * @param evict false when initially constructing this map, else
 * true (relayed to method afternodeinsertion).
 */
final void putmapentries(map<? extends k, ? extends v> m, boolean evict) {
    int s = m.size();
    if (s > 0) {
        // 判断table是否已经初始化
        if (table == null) { // pre-size
             // 未初始化,s为m的实际元素个数
            float ft = ((float)s / loadfactor) + 1.0f;
            int t = ((ft < (float)maximum_capacity) ?
                     (int)ft : maximum_capacity);
            // 计算得到的t大于阈值,则初始化阈值
            if (t > threshold)
                threshold = tablesizefor(t);
        }
        // 已初始化,并且m元素个数大于阈值,进行扩容处理
        else if (s > threshold)
            resize();
        // 将m中的所有元素添加至hashmap中
        for (map.entry<? extends k, ? extends v> e : m.entryset()) {
            k key = e.getkey();
            v value = e.getvalue();
            putval(hash(key), key, value, false, evict);
        }
    }
}
/**
 * 扩容
 * ①.在jdk1.8中,resize方法是在hashmap中的键值对大于阀值时或者初始化时,就调用resize方法进行扩容;
 * ②.每次扩展的时候,都是扩展2倍:16、32、64、128...
*  ③.扩展后node对象的位置要么在原位置,要么移动到原偏移量两倍(2次幂)的位置。
 * @return the table
 */
final node<k,v>[] resize() {
    node<k,v>[] oldtab = table;//oldtab指向hash桶数组
    int oldcap = (oldtab == null) ? 0 : oldtab.length;
    int oldthr = threshold;
    int newcap, newthr = 0;
    if (oldcap > 0) {//如果oldcap大于的话,就是hash桶数组不为空
        if (oldcap >= maximum_capacity) {//如果大于最大容量了,就赋值为整数最大的阀值
            threshold = integer.max_value;
            return oldtab;
        }
        //如果当前hash桶数组的长度在扩容后仍然小于最大容量 并且oldcap大于默认值16,就扩充为原来的2倍
        else if ((newcap = oldcap << 1) < maximum_capacity &&
                 oldcap >= default_initial_capacity)
            newthr = oldthr << 1; // double threshold 双倍扩容阀值threshold
    }
    else if (oldthr > 0) // initial capacity was placed in threshold
        newcap = oldthr;
    else {               // zero initial threshold signifies using defaults
        newcap = default_initial_capacity;
        newthr = (int)(default_load_factor * default_initial_capacity);
    }
    //计算新的resize上限
    if (newthr == 0) {
        float ft = (float)newcap * loadfactor;
        newthr = (newcap < maximum_capacity && ft < (float)maximum_capacity ?
                  (int)ft : integer.max_value);
    }
    threshold = newthr;
    @suppresswarnings({"rawtypes","unchecked"})
        node<k,v>[] newtab = (node<k,v>[])new node[newcap];//新建hash桶数组
    table = newtab;//将新数组的值复制给旧的hash桶数组
    if (oldtab != null) {//进行扩容操作,复制node对象值到新的hash桶数组
        //把每个bucket都移动到新的buckets中
        for (int j = 0; j < oldcap; ++j) {
            node<k,v> e;
            if ((e = oldtab[j]) != null) {//如果旧的hash桶数组在j结点处不为空,复制给e
                oldtab[j] = null;//将旧的hash桶数组在j结点处设置为空,方便gc
                if (e.next == null)//如果e后面没有node结点
                    newtab[e.hash & (newcap - 1)] = e;//直接对e的hash值对新的数组长度求模获得存储位置
                else if (e instanceof treenode)//如果e是红黑树的类型,那么添加到红黑树中
                    ((treenode<k,v>)e).split(this, newtab, j, oldcap);
                else { // preserve order 链表优化重hash的代码块
                    node<k,v> lohead = null, lotail = null;
                    node<k,v> hihead = null, hitail = null;
                    node<k,v> next;
                    do {
                        next = e.next;//将node结点的next赋值给next
                        if ((e.hash & oldcap) == 0) {//如果结点e的hash值与原hash桶数组的长度作与运算为0   原索引
                            if (lotail == null)//如果lotail为null
                                lohead = e;//将e结点赋值给lohead
                            else
                                lotail.next = e;//否则将e赋值给lotail.next
                            lotail = e;//然后将e复制给lotail
                        }
                        // 原索引+oldcap
                        else {//如果结点e的hash值与原hash桶数组的长度作与运算不为0
                            if (hitail == null)//如果hitail为null
                                hihead = e;//将e赋值给hihead
                            else
                                hitail.next = e;//如果hitail不为空,将e复制给hitail.next
                            hitail = e;//将e复制个hitail
                        }
                    } while ((e = next) != null);//直到e为空
                    //原索引放到bucket里
                    if (lotail != null) {//如果lotail不为空
                        lotail.next = null;//将lotail.next设置为空
                        newtab[j] = lohead;//将lohead赋值给新的hash桶数组[j]处
                    }
                    //原索引+oldcap放到bucket里
                    if (hitail != null) {//如果hitail不为空
                        hitail.next = null;//将hitail.next赋值为空
                        newtab[j + oldcap] = hihead;//将hihead赋值给新的hash桶数组[j+旧hash桶数组长度]
                    }
                }
            }
        }
    }
    return newtab;
}

     3)v putifabsent(k key, v value);//如果key存在,则跳过,不覆盖value值,onlyifabsent传true,不覆盖更改现有值

/**
 * 如果key存在则跳过,不覆盖value值,onlyifabsent传true,不覆盖更改现有值
 * 如果key不存在则put
 * @param key
 * @param value
 * @return
 */
@override
public v putifabsent(k key, v value) {
    return putval(hash(key), key, value, true, true);
}

     4)merge(k key, v value,bifunction<? super v, ? super v, ? extends v> remappingfunction);//用某种方法更新原来的value值

/**
 * 用某种方法更新原来的value值
 * bifunction支持函数式变成,lambda表达式,如:string::concat拼接
 * @param key
 * @param value
 * @param remappingfunction
 * @return
 */
@override
public v merge(k key, v value,
               bifunction<? super v, ? super v, ? extends v> remappingfunction) {
    if (value == null)
        throw new nullpointerexception();
    if (remappingfunction == null)
        throw new nullpointerexception();
    int hash = hash(key);
    node<k,v>[] tab; node<k,v> first; int n, i;
    int bincount = 0;
    treenode<k,v> t = null;
    node<k,v> old = null;// 该key原来的节点对象
    if (size > threshold || (tab = table) == null ||
        (n = tab.length) == 0)//判断是否需要扩容
        n = (tab = resize()).length;
    if ((first = tab[i = (n - 1) & hash]) != null) {
        if (first instanceof treenode)// 取出old node对象
            old = (t = (treenode<k,v>)first).gettreenode(hash, key);
        else {
            node<k,v> e = first; k k;
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                    old = e;
                    break;
                }
                ++bincount;
            } while ((e = e.next) != null);
        }
    }
    if (old != null) {//如果 old node 存在
        v v;
        if (old.value != null)
            // 如果old存在,执行lambda,算出新的val并写入old node后返回。
            v = remappingfunction.apply(old.value, value);
        else
            v = value;
        if (v != null) {
            old.value = v;
            afternodeaccess(old);
        }
        else
            removenode(hash, key, null, false, true);
        return v;
    }
    if (value != null) {
        //如果old不存在且传入的newval不为null,则put新的kv
        if (t != null)
            t.puttreeval(this, tab, hash, key, value);
        else {
            tab[i] = newnode(hash, key, value, first);
            if (bincount >= treeify_threshold - 1)
                treeifybin(tab, hash);
        }
        ++modcount;
        ++size;
        afternodeinsertion(true);
    }
    return value;
}

     5)compute(k key,bifunction<? super k, ? super v, ? extends v> remappingfunction);//根据已知的 k v 算出新的v并put

/**
 * 根据已知的 k v 算出新的v并put。
 * 如果根据key获取的oldval为空则lambda中涉及到oldval的计算会报空指针。
 * 如:map.compute("a", (key, oldval) -> oldval + 1); 如果oldval为null,则空指针
 * 源码和merge类似
 * bifunction返回值作为新的value,bifunction有二个参数
 * @param key
 * @param remappingfunction
 * @return
 */
@override
public v compute(k key,
                 bifunction<? super k, ? super v, ? extends v> remappingfunction) {
    if (remappingfunction == null)
        throw new nullpointerexception();
    int hash = hash(key);
    node<k,v>[] tab; node<k,v> first; int n, i;
    int bincount = 0;
    treenode<k,v> t = null;
    node<k,v> old = null;
    if (size > threshold || (tab = table) == null ||
        (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((first = tab[i = (n - 1) & hash]) != null) {
        if (first instanceof treenode)
            old = (t = (treenode<k,v>)first).gettreenode(hash, key);
        else {
            node<k,v> e = first; k k;
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                    old = e;
                    break;
                }
                ++bincount;
            } while ((e = e.next) != null);
        }
    }
    v oldvalue = (old == null) ? null : old.value;
    v v = remappingfunction.apply(key, oldvalue);
    if (old != null) {
        if (v != null) {
            old.value = v;
            afternodeaccess(old);
        }
        else
            removenode(hash, key, null, false, true);
    }
    else if (v != null) {
        if (t != null)
            t.puttreeval(this, tab, hash, key, v);
        else {
            tab[i] = newnode(hash, key, v, first);
            if (bincount >= treeify_threshold - 1)
                treeifybin(tab, hash);
        }
        ++modcount;
        ++size;
        afternodeinsertion(true);
    }
    return v;
}

     6)computeifabsent(k key, function<? super k, ? extends v> mappingfunction);//当key不存在时才put,如果key存在则无效

 /**
 * 当key不存在时才put,如果key存在则无效
 * 如:computeifabsent(keyc, k -> genvalue(k));
 * function返回值作为新的value,function只有一个参数
 * @param key
 * @param mappingfunction
 * @return
 */
@override
public v computeifabsent(k key,
                         function<? super k, ? extends v> mappingfunction) {
    if (mappingfunction == null)
        throw new nullpointerexception();
    int hash = hash(key);
    node<k,v>[] tab; node<k,v> first; int n, i;
    int bincount = 0;
    treenode<k,v> t = null;
    node<k,v> old = null;
    if (size > threshold || (tab = table) == null ||
        (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((first = tab[i = (n - 1) & hash]) != null) {
        if (first instanceof treenode)
            old = (t = (treenode<k,v>)first).gettreenode(hash, key);
        else {
            node<k,v> e = first; k k;
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                    old = e;
                    break;
                }
                ++bincount;
            } while ((e = e.next) != null);
        }
        v oldvalue;
        if (old != null && (oldvalue = old.value) != null) {
            afternodeaccess(old);
            return oldvalue;
        }
    }
    v v = mappingfunction.apply(key);
    if (v == null) {
        return null;
    } else if (old != null) {
        old.value = v;
        afternodeaccess(old);
        return v;
    }
    else if (t != null)
        t.puttreeval(this, tab, hash, key, v);
    else {
        tab[i] = newnode(hash, key, v, first);
        if (bincount >= treeify_threshold - 1)
            treeifybin(tab, hash);
    }
    ++modcount;
    ++size;
    afternodeinsertion(true);
    return v;
}

     7)computeifpresent(k key, bifunction<? super k, ? super v, ? extends v> remappingfunction);//compute方法的补充,如果key存在,则覆盖新的bifunction计算出的value值,如果不存在则跳过

/**
 * compute方法的补充,如果key存在,则覆盖新的bifunction计算出的value值,如果不存在则跳过
 * @param key
 * @param remappingfunction
 * @return
 */
public v computeifpresent(k key,
                          bifunction<? super k, ? super v, ? extends v> remappingfunction) {
    if (remappingfunction == null)
        throw new nullpointerexception();
    node<k,v> e; v oldvalue;
    int hash = hash(key);
    if ((e = getnode(hash, key)) != null &&
        (oldvalue = e.value) != null) {
        v v = remappingfunction.apply(key, oldvalue);
        if (v != null) {
            e.value = v;
            afternodeaccess(e);
            return v;
        }
        else
            removenode(hash, key, null, false, true);
    }
    return null;
}

     8)v replace(k key, v value);//替换新值

/**
 * 如果key存在不为空,则替换新的value值
 */
@override
public v replace(k key, v value) {
    node<k,v> e;
    if ((e = getnode(hash(key), key)) != null) {
        v oldvalue = e.value;
        e.value = value;
        afternodeaccess(e);
        return oldvalue;
    }
    return null;
}

     9)replace(k key, v oldvalue, v newvalue);//判断oldvalue是否是当前key的值,再替换新值

/**
 * 如果key存在不为空并且oldvalue等于当前key的值,则替换新的value值
 */
@override
public boolean replace(k key, v oldvalue, v newvalue) {
    node<k,v> e; v v;
    if ((e = getnode(hash(key), key)) != null &&
        ((v = e.value) == oldvalue || (v != null && v.equals(oldvalue)))) {
        e.value = newvalue;
        afternodeaccess(e);
        return true;
    }
    return false;
}

     10)replaceall(bifunction<? super k, ? super v, ? extends v> function);//替换新值

/**
 * 根据lambda函数替换符合规则的值
 */
@override
public void replaceall(bifunction<? super k, ? super v, ? extends v> function) {
    node<k,v>[] tab;
    if (function == null)
        throw new nullpointerexception();
    if (size > 0 && (tab = table) != null) {
        int mc = modcount;
        for (int i = 0; i < tab.length; ++i) {
            for (node<k,v> e = tab[i]; e != null; e = e.next) {
                e.value = function.apply(e.key, e.value);
            }
        }
        if (modcount != mc)
            throw new concurrentmodificationexception();
    }
}

总结:
   正常情况下会扩容2倍,特殊情况下(新扩展数组大小已经达到了最大值)则只取最大值1 << 30。
   

2.remove方法(2种重载实现)--删

HashMap源码分析--jdk1.8

     1)v remove(object key); //根据key 删除元素     

/**
 * 根据key 删除元素
 */
public v remove(object key) {
    node<k,v> e;
    return (e = removenode(hash(key), key, null, false, true)) == null ?
        null : e.value;
} 
 /**
 * implements map.remove and related methods
 *
 * @param hash hash for key
 * @param key the key
 * @param value the value to match if matchvalue, else ignored
 * @param matchvalue if true only remove if value is equal
 * @param movable if false do not move other nodes while removing
 * @return the node, or null if none
 */
final node<k,v> removenode(int hash, object key, object value,
                           boolean matchvalue, boolean movable) {
    node<k,v>[] tab; node<k,v> p; int n, index;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (p = tab[index = (n - 1) & hash]) != null) {
        node<k,v> node = null, e; k k; v v;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            node = p;
        else if ((e = p.next) != null) {
            if (p instanceof treenode)
                node = ((treenode<k,v>)p).gettreenode(hash, key);
            else {
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key ||
                         (key != null && key.equals(k)))) {
                        node = e;
                        break;
                    }
                    p = e;
                } while ((e = e.next) != null);
            }
        }
        if (node != null && (!matchvalue || (v = node.value) == value ||
                             (value != null && value.equals(v)))) {
            if (node instanceof treenode)
                ((treenode<k,v>)node).removetreenode(this, tab, movable);
            else if (node == p)
                tab[index] = node.next;
            else
                p.next = node.next;
            ++modcount;
            --size;
            afternoderemoval(node);
            return node;
        }
    }
    return null;
}

     2)remove(object key, object value); //根据key,value 删除元素 

 /**
 * 根据key,value 删除元素 
 */
   @override
public boolean remove(object key, object value) {
    return removenode(hash(key), key, value, true, true) != null;
}

3.get方法(2种实现)--查

HashMap源码分析--jdk1.8

/**
 * 返回指定的值
 */
     public v get(object key) {
                node<k,v> e;
                return (e = getnode(hash(key), key)) == null ? null : e.value;
        }

/**
* @author jiaxiaoxian
* @date 2019年2月12日 
* 如果key不为空则返回key的值,否则返回默认值
 */
    @override
        public v getordefault(object key, v defaultvalue) {
                node<k,v> e;
                return (e = getnode(hash(key), key)) == null ? defaultvalue : e.value;
        }

4.keyset方法--返回所有key,以set结构返回

/**
 * 返回所有key,以set结构返回
 */
public set<k> keyset() {
    set<k> ks;
    return (ks = keyset) == null ? (keyset = new keyset()) : ks;
}

5.clone方法--克隆

 /**
 * 复制,返回此hashmap 的浅拷贝
 */
@suppresswarnings("unchecked")
@override
public object clone() {
    hashmap<k,v> result;
    try {
        result = (hashmap<k,v>)super.clone();
    } catch (clonenotsupportedexception e) {
        // this shouldn't happen, since we are cloneable
        throw new internalerror(e);
    }
    result.reinitialize();
    result.putmapentries(this, false);
    return result;
}

7.clear方法--清空hashmap

/**
 * 清空hashmap
 */
public void clear() {
    node<k,v>[] tab;
    modcount++;
    if ((tab = table) != null && size > 0) {
        size = 0;
        for (int i = 0; i < tab.length; ++i)
            tab[i] = null;
    }
}

8.containskey方法--是否存在此key

/**
 * 是否存在此key
 */
  public boolean containskey(object key) {
    return getnode(hash(key), key) != null;
}

9.containsvalue方法--是否存在此value

/**
 * 是否存在此value
 */
  public boolean containsvalue(object value) {
    node<k,v>[] tab; v v;
    if ((tab = table) != null && size > 0) {
        for (int i = 0; i < tab.length; ++i) {
            for (node<k,v> e = tab[i]; e != null; e = e.next) {
                if ((v = e.value) == value ||
                    (value != null && value.equals(v)))
                    return true;
            }
        }
    }
    return false;
}

10.entryset方法--返回key,value的set结果

/**
 * 返回key,value的set结果
 */
public set<map.entry<k,v>> entryset() {
    set<map.entry<k,v>> es;
    return (es = entryset) == null ? (entryset = new entryset()) : es;
}

hashmap总结

1)hashmap的key、value都可以存放null,key不可重复,value可重复,是数组链表红黑树的数据结构。
2)hashmap区别于数组的地方在于能够自动扩展大小,其中关键的方法就是resize()方法,扩容为2倍。
3)hashmap由于本质是数组,在不冲突的情况下,查询效率很高,hash冲突后会形成链表,查找时多一层遍历,当链表长度到8并且数组长度大于64,转成红黑树存储,提高查询效率。
4)初始化数组时推荐给初始长度,反复扩容会增加时耗,影响性能效率,hashmap需要注意负载因子0.75f,初始16,当长度大于(16*0.75)12的时候会扩容为32,所以初始长度设置需要却别对待。


©著作权归作者所有:来自51cto博客作者jiazhipeng12的原创作品,如需转载,请注明出处,否则将追究法律责任