zoukankan      html  css  js  c++  java
  • 基于JDK1.8版本的hashmap源码分析(一)

    今天看了下hashmap中的源码,下面列出一些自己的收获

    开头,public class HashMap<K,V> extends AbstractMap<K,V>    implements Map<K,V>, Cloneable, Serializable { //继承子AbstractMap类,实现了克隆和序列化

      static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16   //表示初始化定义的容量是16,通过移位实现的,1是2^0次方,左移4位就是2^4=16,这个值必须是2的次方

      static final int MAXIMUM_CAPACITY = 1 << 30;//定义的最大容量,是2^30次方,这个值必须是2的次方(计算机采用二进制处理数据,二进制就2个数据0,1,可以表示多种含义,实现简单)

      static final float DEFAULT_LOAD_FACTOR = 0.75f;这个是加载因子,表示容器被使用75%时,就进行扩容了。是一种容器使用量的控制

      static final int TREEIFY_THRESHOLD = 8;表示当链表的长度为8时就把链表转换为红黑树,此值是定义的临界转换值

      static final int UNTREEIFY_THRESHOLD = 6;表示重新设置把树改为链表,当长度小于6时

      static final int MIN_TREEIFY_CAPACITY = 64;桶被树形化时的表最小容量,或者决定对桶进行扩容。即根据hash表中的数据决定是树形化还是扩容。

        static class Node<K,V> implements Map.Entry<K,V> {//此处是定义静态内部类的结点,需要hash值(用于计算存放到hashmap中的哪个位置),key值(标识),value,next(下一个结点)
            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;
            }
    
            public final K getKey()        { return key; }
            public final V getValue()      { return value; }
            public final String toString() { return key + "=" + value; }
    
    /**
          *此处是重写Object类中的hashCode值,为了是分布均匀,尽可能减少冲突,不保证唯一,但同一个类,必须有唯一的hashcode,不同的对象可以有相同的hashcode值
          *使用key和value的值进行hashcode值的异或决定放到桶中的哪个位置上
         */ 
    public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; }
        /**一般重写hashcode方法后,都会重写equals方法,2个都一起进行修改,equals具体是比较两个对象是否相等,==是比较地址是否相等的
         *而不同的对象一定会有不同的内存地址,导致2对象的比较一定不等,重写Object类中的equals方法后(Object类中直接比较地址),会在instanceof判断是同一个类型后,比较对象内部的属性
         *若属性相同,则判断这两个对象是相等的,equals方法返回true,当然前提是hashcode定位到桶的哪个位置,在桶的链表存储中使用equals进行具体的对象比较。
        */
    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; } }

    //此处是获取hash值的方法,若key值不为空时,使用key值与key无符号右移16位的值进行按位异或的操作得到的结果。可以进来得到高低hash值的分布,高位与地位都有数据,
        static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }

    /**
    *此处定义的是静态类:比较器,主要是返回参数对象x的类
    *前提是参数对象要实现了Comparable接口,否则返回空,
    */
        static Class<?> comparableClassFor(Object x) {
            if (x instanceof Comparable) { //判断x是否是实现了Comparable接口,若没有实现此接口,则直接返回null
                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;
        }
    /**
    *此处是返回对象k,x的比较结果,
    *前提是对象x的类不匹配kc的类,否则返回结果0
    */
        static int compareComparables(Class<?> kc, Object k, Object x) {
            return (x == null || x.getClass() != kc ? 0 :
                    ((Comparable)k).compareTo(x));
        }

    /**
    *此处是实现表的大小设置,
    *n无符号右移,移位后与本身按位或,依次是无符号右移2的n次方,最终返回的是表的设置大小。
    */
        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;
        }

        transient Node<K,V>[] table;//表在第一次使用时初始化,长度是2的N次方
        /**
         * 用于存储结点的定义
         */
      transient Set<Map.Entry<K,V>> entrySet;
        /**
         *一整个map中存储多少个key-value键值对,map中存储的都是key-value映射关系的数据
         */
        transient int size;
        /**
         * 下一次容器扩容的大小.
         */
        int threshold;
        /**
         * hash表的加载因子,是说明当容器使用了(容量*加载因子)的容量时,进行扩容,默认的加载因子是0.75,意思是当容器的容量是100时,当使用了75个就可以进行扩容了,扩容后面会有说明
         */
        final float loadFactor;

       /**
         * 使用构造器构造一个空的hashmap对象,使用自定义的初始容量和加载因子
         *
         * @param  初始化容量
         * @param  加载因子
         */
        public HashMap(int initialCapacity, float loadFactor) {
            if (initialCapacity < 0) //初始化容器的容量不能小于0
                throw new IllegalArgumentException("Illegal initial capacity: " +
                                                   initialCapacity);
            if (initialCapacity > MAXIMUM_CAPACITY)//初始化容器的容量不能大于最大值。
                initialCapacity = MAXIMUM_CAPACITY;
            if (loadFactor <= 0 || Float.isNaN(loadFactor))//加载因子不能小于等于0,也不能是未确定的
                throw new IllegalArgumentException("Illegal load factor: " +
                                                   loadFactor);
            this.loadFactor = loadFactor;
            this.threshold = tableSizeFor(initialCapacity);
        }

    //以下几个构造器就不再重复说明了

        /**
         * 实现在map中放入对象,
         */
        final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
            int s = m.size(); //获取当前map的空间大小
            if (s > 0) { //若hashmap大于0
                if (table == null) { // 检查表是否是还没有初始化
                    float ft = ((float)s / loadFactor) + 1.0F;
                    int t = ((ft < (float)MAXIMUM_CAPACITY) ?//检测是否容量大于最大定义的容器容器,若大于则去容器的最大容量值
                             (int)ft : MAXIMUM_CAPACITY);
                    if (t > threshold) //判断是否大于最大阀值,若是则重新初始化表的大小
                        threshold = tableSizeFor(t);
                }
                else if (s > threshold) //若表已经初始化,且hash表的大小大于定义的值,就会重新进行设置大小
                    resize();
                for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {//循环把对象放入到map中
                    K key = e.getKey();
                    V value = e.getValue();
                    putVal(hash(key), key, value, false, evict);//实现具体放到map中的方法,后续会有说明
                }
            }
        }

    //返回map中key对象的value值
        public V get(Object key) {
            Node<K,V> e;
            return (e = getNode(hash(key), key)) == null ? null : e.value;
        }

    /**具体实现怎么根据key值获取具体的value值,传入的有hash值和key,
    *根据hash值可以计算出具体的table中的哪个下标,使用的计算方式是:(n - 1) & hash,n代表数组的长度,效果和hash%n是一样的效果,不过移位计算的效果更好
    *先查找第一个结点是不是相等的,若相等,则直接返回,若不是则判断是不是树结点,若是,则循环树,获取具体的结点信息
    *最后,循环遍历链表,在链表中返回对应key的结点信息
    */
        final Node<K,V> getNode(int hash, Object key) {
            Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
            if ((tab = table) != null && (n = tab.length) > 0 &&
                (first = tab[(n - 1) & hash]) != null) {
                if (first.hash == hash && // 每次都会检测都first结点
                    ((k = first.key) == key || (key != null && key.equals(k))))//若第一个结点符合条件,则直接返回
                    return first;
                if ((e = first.next) != null) {//若不等
                    if (first instanceof TreeNode) //后续是否有树结点
                        return ((TreeNode<K,V>)first).getTreeNode(hash, key);//若是树,则返回树结点的对应值
                    do {//循环链表,找出匹配的key值,返回对象的引用结点
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            return e;
                    } while ((e = e.next) != null);
                }
            }
            return null;
        }
  • 相关阅读:
    .net framework 3.5 dotNetFx35setup 能不需要网络支持吗? 
    Android API Differences Report
    Android用户版本分布更新 2.1版领先
    Adobe升级Flash回击批评:流畅播放手机视频
    iPhone OS4.0,Android 2.1和WP7对比分析
    Android 2.2数据共享功能开启与否将由运营商自主决定
    谷歌 Android 3.0计划四季度推出:代号姜饼
    谷歌称Android设备日激活量已达10万台
    iPod Touch也将支持运行Android系统
    Android2.2 SDK正式提供下载
  • 原文地址:https://www.cnblogs.com/guopengxia0719/p/10604964.html
Copyright © 2011-2022 走看看