hashmap源码分析1

之前看过hashmap源码,感觉最近有些遗忘,写一波复习一遍,首先查看hashmap的继承体系,可以发现hashMap实现了map,cloneble,serializable接口这些接口应该都很熟悉了吧,其次hashmap继承自abstractmap查看这个父类,发现他是一个抽象类,因此其中有些方法是可以复写的,具体方法查看源码可以发现包括entrySet其他方法可以看做map这种数据结构都通用的方法。
接下来看下hashmap重要属性,我们从前到后依次查看第一个是
    private static final long serialVersionUID = 362498820763181265L;
这个是关于类序列化的,如果忘了,可以查看具体知识
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
hashmap的默认初始化容量,说的通俗点就是你创建了hashmap之后,初始情况下的容量,这里用了位运算十进制为16,之所以这样这样子运算速度较快
static final int MAXIMUM_CAPACITY = 1 << 30;
这是hashmap的最大容量,就是你创建的hashmap最多只能这么大,超过这么大系统可能就不能创建了,至于这个值也是用的位运算,结果是2的30次方具体多少赶你兴趣的可以去计算计算。
static final float DEFAULT_LOAD_FACTOR = 0.75f;
下一个float类型答案是0.75.行话说的是反应当前hashmap满的程度,感觉说的还是让初次接触的人云里雾里的,其实说白了可以这么认为不是你hashmap创建以后有一个默认长度吗,就拿16来说,其实这个长度是可以你指定的,但是你设置以后,长度就是这么多了,这长度其实是一个暂时的容量,然后你每给这hashmap里放一个东西这破玩意就会占用一个容量当容量不足时,hashmap这东西可以自动扩容,这是个很神奇的机制,后面再说,而这个负载因子呢他就是反映了你放了值占得容量和总容量的比,就是个百分比。也没什么其他的了
static final int TREEIFY_THRESHOLD = 8;
这个学名叫树化阙值,听名字是不是觉得贼高大上,其实就是hashmap为了提高查找效率做的一些手脚,要想理解这个首先得讲下hashmap的基本构造,让我给大家画个图:

hashmap源码分析1

看到横着的那一排格子吗,你每次往hashmap里面放值他都要先计算一下key的hash值hash值一样的连城一排,传说中的拉链地址法(应该没有记错)如果有人不了解什么是key,hash我建议你别看了,因为再看你也看不懂,好好看看基础的,好了扯远了,回头继续说,每次都给hash值一样的那样排列这样越来越长,每次查找耗费的时间也越长,所以为了提高效率,大牛们想到一个让我这等菜鸡汗颜的方法,他把线型转化成树形,就是那个让我现在还觉得挺奇妙的红黑树,或许会有人有疑问既然树形效率高为嘛不直接用树形,这个问题我觉得吧得看情况,而且转化你不得有消耗啊。介绍了这些我们再说这个树化阙值,他就是那个决定什么时候要树化的值,我们可以看到值是8也就是说如果那横着一排的数目达到了8,那么就得进行树化了
static final int UNTREEIFY_THRESHOLD = 6;
上面bb了那么多,估计现在理解这个6也不难了吧,他就是把横着的那些变成树形后在变成线型的值,可以看到值是6
static final int MIN_TREEIFY_CAPACITY = 64;
直接翻译最小树化容量,说的在直白点就是变成树形后默认的容量默认为64源码注释里说至少是4*树化阙值,就是那个8所以算下来最小情况下是32至于设置成64有什么讲究,如果有懂的大佬可以说一下,让我这个菜鸡也学习学习。
static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;


        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
下来是一个内部类这个玩意就相当于我画的那一个长方形,他也算是hashmap的基本组成单位。这个node类里面有几个方法,比较简单,就不解释了,感兴趣的自己可以去看看。
 static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
下来看hashmap里面的方法,第一个是计算key的hash值,计算方法是先判断key是否为空,如果为空返回0,不是空拿到key的hashcode并与h右移进行抑或计算。
 static Class<?> comparableClassFor(Object x) {
        if (x instanceof Comparable) {
            Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
            if ((c = x.getClass()) == String.class) // bypass checks
                return c;
            if ((ts = c.getGenericInterfaces()) != null) {
                for (int i = 0; i < ts.length; ++i) {
                    if (((t = ts[i]) instanceof ParameterizedType) &&
                        ((p = (ParameterizedType)t).getRawType() ==
                         Comparable.class) &&
                        (as = p.getActualTypeArguments()) != null &&
                        as.length == 1 && as[0] == c) // type arg is c
                        return c;
                }
            }
        }
        return null;
    }
返回x对象的类类型,首先判断对象x是否可比较,如果没有返回空,如果属于判断x的类类型是否是字符串类型如果是返回x的类类型,如果类实现的接口不为空遍历每个接口判断每个接口是否属于ParamterizedType类型并且它的参数类型是否是Comparable类并且得到的世界参数类型不为空并且as的长度是否为并且as的类是否属于c,然后返回c
 static int compareComparables(Class<?> kc, Object k, Object x) {
        return (x == null || x.getClass() != kc ? 0 :
                ((Comparable)k).compareTo(x));
    }
判断对象x是否为空或对象x的类类型是否属于kc条件成立返回0否则返回k与x的比较值,其实看注释更好理解就是如果x的对象与kc匹配则使用compareTo方法否则返回0.
 static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
将目标容量变为2的次方类型。


下面在介绍几种属性,没办法我是按着代码顺序看的,可能有点乱,大家不要介意,
 transient Node<K,V>[] table;
作为一个初始化的容器,用来存储基本node修饰属性为transient即不可序列化。
transient Set<Map.Entry<K,V>> entrySet;
Holds cached entrySet(). Note that AbstractMap fields are used
     * for keySet() and values().
hashmap拥有的一个缓存entryset.注意abstractmap属性为keySet和values使用。
transient int size;
就是之前说的目前占得容量
transient int modCount;
记录创建的hashmap改动的次数。
int threshold;
这个个属性也是容量,我们该怎么理解呢,其实他就是你目前hashmap的容量和你的负载因子的乘积。
final float loadFactor;
呐这个是负载因子,之前那个是默认的但是呢我们也可以给它指定值,大牛就是聪明,想的就是周到哈哈。好了暂时分析这么多,有空更新关于hashmap的重要方法,今天先去休息了