Android HashMap详解

Android HashMap详解

这个源代码解析是Android-30sdk内的代码解析.

HashMap是健值对的方式来进行存储数据,正常初始化,添加数据,获取数据,清除集合

        // 初始化
        Map<String,String> map = new HashMap<>();
        // 添加数据
        map.put("username","hello");
        map.put("email","123456@qq.com");
        // 读取数据
        String username = map.get("username");
        String email = map.get("email");
        // 清空集合
        map.clear();

初始化new HashMap<>();

    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
    public HashMap() { 
    	// 初始化加载因子,因为HashMap初始化的时候未做设置,所以设置加载因子为默认值 0.75
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

其他的构造方法,可传入初始容量,加载因子

    public HashMap(int initialCapacity) { // 如果有进行设置初始容量直接进行调用另一个构造方法
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    // 默认最大容量值
    static final int MAXIMUM_CAPACITY = 1 << 30;
    
    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 或者不为float值 抛出异常
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor; // 设置加载因子
        this.threshold = tableSizeFor(initialCapacity); // 设置阈值
    }

tableSizeFor(initialCapacity)

	// 主要是将传入的初始化容量转为2的幂
    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;
    }

例如我们传入的是20,
int n = cap - 1;// 19
转为二进制位 00010011
n |= n >>> 1; 先进行计算向右无符号移动一位 得到数据为 00001001在进行计算 | 运算
n >>> 1 : 00001001 | 00010011 -> 00011011 即 n = 27
n |= n >>> 2;
n >>> 2 : 00000110 | 00011011 -> 00011111 即 n = 31
n |= n >>> 4;
n >>> 4 : 00000001 | 00011111 -> 00011111 即 n = 31
n |= n >>> 8;
n >>> 8 : 00000000 | 00011111 -> 00011111 即 n = 31
n |= n >>> 16;
n >>> 16 : 00000000 | 00011111 -> 00011111 即 n = 31
最终返回得到的值为 n+1 = 32 ;
初始化

  1. 如果初始化中未传入初始容量和加载因子,HashMap使用默认容量16,默认加载因子 0.75
  2. 如果初始化中传入初始容量和加载因子,加载因子直接赋值给成员变量, 初始容量会进行处理,处理成为2的幂的值 例如 20 -> 32

添加数据 map.put(“username”,“hello”);

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

hash(key) 获取key对象的hash值

    static final int hash(Object key) {
        int h;
        // 如果key为null直接返回0值 
        // 如果key不为空即获取key.hashCode()值,将得到的值进行处理
        // 因为hashCode是int类型 int类型是4字节的长度 h >>> 16 无符号向右进行移动16位 得到高位所有数据
        // 将高位的hashCode与地位的hashCode进行异或操作
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

hash()方法将key中的hashCode值进行高位与低位的异或操作得到新的hash值. key为空则hash返回0

putVal(hash(key), key, value, false, true);
	// onlyIfAbsent 如果当前位置已存在一个值,是否替换,false是替换,true是不替换 
	// evict 表是否在创建模式,如果为false,则表是在创建模式。
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0) // 如果集合table的值位null,或者是table的长度为0
            n = (tab = resize()).length; // 设置table大小 并获取table.length
        if ((p = tab[i = (n - 1) & hash]) == null) // 如果当前下标的节点为null
            tab[i] = newNode(hash, key, value, null); // 直接进行新节点的添加
        else { // 当前下标的节点不为null
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k)))) // 判断hash值是否与现在的节点一致
                e = p; // 记录当前节点
            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) { // 如果当前节点为null 链表尾部
                        p.next = newNode(hash, key, value, null); // 添加一个node
                        if (binCount >= TREEIFY_THRESHOLD - 1) // 如果当前链表节点已经超过了树节点阈值
                            treeifyBin(tab, hash); // 将当前链表转为红黑树存储结构
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k)))) // 如果当前链表中有相同key的,退出循环
                        break;
                    p = e;
                }
            }
            if (e != null) { // 如果e不为null 则表示当前value非新增 需要进行修改
                V oldValue = e.value; // 将之前旧值取出来
                if (!onlyIfAbsent || oldValue == null) // onlyIfAbsent因为传入的为false,所以这里进行value的赋值
                    e.value = value;
                afterNodeAccess(e); // 成功之后的节点访问
                return oldValue; // 返回之前的旧值
            }
        }
        ++modCount; // 操作数+1
        if (++size > threshold) // size+1 如果大于阈值 则进行扩充
            resize();
        afterNodeInsertion(evict); // 插入成功之后的调用
        return null; // 插入数据则返回null
    }

添加数据

  1. 数据集合为null,或者长度为0,则进行resize()操作
  2. [n - 1] & hash hash值对length-1进行求余,为什么是length-1,因为下标是从0开始的,
    00000000000000001001010101010101 & 00000000000000000000000000011111 ->
    00000000000000000000000000010101 = 21 ,如果hash值为上述这个即保存到下标为21的地方
  3. 判断当前下标头节点的key是否与put中的key一样,一样则将当前节点保存到e变量中.
  4. 与头节点中的key不一致,判断头节点是否是树类型节点,是就进行树节点的操作,对比是否有相同的key,有就返回节点并保存到e变量中,没有就新建一个节点,并返回null,
  5. 不是树类型就进行遍历当前链表上的节点,如果获取到节点为null,则添加节点,并判断当前链表长度是否大于等于8,是就将链表结构转为红黑树结构,否就不管,依旧是链表节点.
  6. 不是树类型进行遍历链表的时候发现与key一致,保存当前节点到e变量中,并退出遍历
  7. 判断e变量是否不为空,不为空表示为需要对旧值进行更改,如果旧值为null或者onlyIfAbsent=false即可设置新值.
  8. 添加节点需要进行modCount+1,和使用大小+1. 如果超过阈值,则进行resize()操作.
    final Node<K,V>[] resize() {// 进行重新设置table集合的大小
        Node<K,V>[] oldTab = table; // 获取之前的table
        int oldCap = (oldTab == null) ? 0 : oldTab.length; // 获取之前的容量
        int oldThr = threshold; // 获取阈值
        int newCap, newThr = 0; // 创建新的容量和阈值
        if (oldCap > 0) { // 如果table不为空 并length大于0
            if (oldCap >= MAXIMUM_CAPACITY) { // 判断旧容量是否大于等于最大容量值 1 << 30
                threshold = Integer.MAX_VALUE; // 如果是大于等于最大容量大 即设置为Integer最大值 2的31次幂 -1。2147483647
                return oldTab; // 直接返回旧table
            }
            // 如果旧容量小于最大容量 旧容量的一倍小于最大容量 并且 旧容量要大于等于默认容量(16) 即执行下面操作
            // 新的容量 = 旧的容量 * 2
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold 初始化容量将阈值设置给容量
            newCap = oldThr; // 如果上面传入的容量是20 旧的阈值即为32
        else {               // zero initial threshold signifies using defaults 0阈值 就使用默认的容量
            newCap = DEFAULT_INITIAL_CAPACITY; // 新容量为 = 16
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); // 新阈值为 = 0.75 * 16 = 12
        }
        if (newThr == 0) { // 如果新得到的阈值为0
            float ft = (float)newCap * loadFactor; // ft = 16 * 0.75 = 12
            // 判断新的容量小于最大容量 并且 ft 小于最大容量 为真 就使用ft 为假 就赋值Integer最大值
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr; // 将新的阈值赋值给成员变量 threshold
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; // 创建固定长度的集合
        table = newTab; // 将新创建的集合赋值给成员变量table
        if (oldTab != null) {// 如果旧集合不为空的情况
            for (int j = 0; j < oldCap; ++j) { // 对集合进行遍历
                Node<K,V> e;
                if ((e = oldTab[j]) != null) { // 判断当前节点不为null 进行处理
                    oldTab[j] = null; // 对旧的集合节点置空
                    if (e.next == null) // 获取集合中下标为j的下一个节点是否为空
                     	// 如果只有一个节点,之间将当前所有的节点通过hash碰撞的到存放位置的下标并赋值到对应新的集合中的下标中
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode) // 如果当前节点为树节点
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order 维持秩序
                        Node<K,V> loHead = null, loTail = null; // 低下标的节点
                        Node<K,V> hiHead = null, hiTail = null; // 高下标的节点
                        Node<K,V> next; // 下一个数据
                        do { // do while 会先进入循环执行一次之后再进行判断要求是否符合
                            next = e.next; // 获取到当前节点的链表结构的头节点
                            if ((e.hash & oldCap) == 0) { // 如果头节点的hash值与就容量进行hash碰撞得到的值为0
                                if (loTail == null) // 如果低下标的尾节点为空,表示第一次进入
                                    loHead = e; // 对低下标的头节点进行赋值
                                else
                                    loTail.next = e; // 非第一次就进行低下标尾部节点添加处理
                                loTail = e;
                            }
                            else { // 如果头节点的hash值与就容量进行hash碰撞得到的值不为0
                                if (hiTail == null) // 判断高下标是否为空,
                                    hiHead = e; // 对高下标头节点进行赋值
                                else // 非第一次进入 进行赋值
                                    hiTail.next = e; // 对高下标尾部进行链表赋值处理
                                hiTail = e; // 将节点赋值给高下标尾部
                            }
                        } while ((e = next) != null); // 一直将当前旧下标中的所有集合进行遍历
                        if (loTail != null) { // 如果低下标尾节点不为空,对下标[j]赋值与低下标的头部
                            loTail.next = null;
                            newTab[j] = loHead; // 赋值处理
                        }
                        if (hiTail != null) { // 如果高下标的尾节点不为空,对下标[oldCap + j]赋值于高下标的头部数据
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead; // 赋值处理
                        }
                    }
                }
            }
        }
        return newTab; // 返回创建之后的新集合
    }
(e.hash & oldCap) == 0 // 节点的hash值与就容量进行hash碰撞得到的值为0

如果e.hash = 15 = 00001111. oldcap = 16 = 00010000
00001111 & 00010000 = 0; 这个时候就表示当前是哪一个下标现在依旧保存在哪一个下标中,为低下标的节点
如果e.hash = 31 = 00011111, oldcap = 16 = 00010000
00011111 & 00010000 = 16; 这个时候就表示当前就集合放在[15]中现在就需要放入到[31] = [oldCap + 15]

((TreeNode<K,V>)e).split(this, newTab, j, oldCap); 对树结构的节点进行遍历,放入到新的集合中
        final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
            TreeNode<K,V> b = this;
            // Relink into lo and hi lists, preserving order
            TreeNode<K,V> loHead = null, loTail = null; // 低下标节点链表
            TreeNode<K,V> hiHead = null, hiTail = null; // 高下标节点列表
            int lc = 0, hc = 0;
            for (TreeNode<K,V> e = b, next; e != null; e = next) {
                next = (TreeNode<K,V>)e.next; // 获取下一个节点数据
                e.next = null; // 将e的next置空,保证只有一个节点
                if ((e.hash & bit) == 0) { // 节点的hash值与旧容量进行hash碰撞是否是 == 0
                    if ((e.prev = loTail) == null) // 0 判断尾节点是否是为空
                        loHead = e; // 低下标的头节点进行赋值
                    else
                        loTail.next = e; // 低下标的尾节点next进行赋值处理
                    loTail = e; // 低下标的尾节点进行赋值处理
                    ++lc; // 低下标的容量+1
                }
                else { // hash与oldCap碰撞不为0
                    if ((e.prev = hiTail) == null) //高下标尾是否为空
                        hiHead = e; // 高下标头节点赋值处理
                    else
                        hiTail.next = e; // 高下标尾节点next赋值处理
                    hiTail = e; // 高下标节点赋值处理
                    ++hc; // 高下标的容量 + 1
                }
            }

            if (loHead != null) { // 低下标头节点不为null
            	// static final int UNTREEIFY_THRESHOLD = 6;
            	// 判断低下标的容量是否小于等于6
                if (lc <= UNTREEIFY_THRESHOLD) 
                    tab[index] = loHead.untreeify(map); // 集合不进行树结构化
                else { // 低下标的容量大于6 需要将链表结构的数据进行树结构化
                    tab[index] = loHead; // 头节点的赋值处理
                    if (hiHead != null) // (else is already treeified) 如果高下标没有数据 则不需要处理 直接使用当前lohead
                        loHead.treeify(tab); // 否则进行数据处理
                }
            }
            if (hiHead != null) { // 判断高下标头节点不为空
                if (hc <= UNTREEIFY_THRESHOLD) // 容量是否小于等于 6
                    tab[index + bit] = hiHead.untreeify(map); // 将树结构节点转为链表结构节点
                else { // 容量大于6
                    tab[index + bit] = hiHead; // 设置集合下标对应的头节点
                    if (loHead != null) // 如果低下标没有数据 及不做处理
                        hiHead.treeify(tab); // 低下标有数据 做处理
                }
            }
        }
loHead.untreeify(map); // 集合不进行树结构化
HashMap -> TreeNode -> untreeify
        final Node<K,V> untreeify(HashMap<K,V> map) {
            Node<K,V> hd = null, tl = null;
            // this为当前树结构节点 即loHead,遍历lohead中所有的树节点转为链表节点,并返回链表节点
            for (Node<K,V> q = this; q != null; q = q.next) {
                Node<K,V> p = map.replacementNode(q, null);
                if (tl == null)
                    hd = p;
                else
                    tl.next = p;
                tl = p;
            }
            return hd; // 返回链表节点头
        }
map.replacementNode(q, null); 将节点类型转为链表节点类型
    Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
        return new Node<>(p.hash, p.key, p.value, next);
    }
loHead.treeify(tab);
        final void treeify(Node<K,V>[] tab) {
            TreeNode<K,V> root = null;
            // this为当前树结构节点 即loHead,遍历lohead中所有的节点
            for (TreeNode<K,V> x = this, next; x != null; x = next) {
                next = (TreeNode<K,V>)x.next; // 拿到当前节点的下一个节点
                x.left = x.right = null; // 对其左右节点进行置空处理
                if (root == null) { // 第一次进入root为null
                    x.parent = null; // 设置x.parent = null
                    x.red = false; // 不是红节点 
                    root = x; // 将x放入根节点
                }
                else { // root != null
                    K k = x.key; // 获取节点的key
                    int h = x.hash; // 获取节点的hash值
                    Class<?> kc = null;
                    for (TreeNode<K,V> p = root;;) { // 死循环
                        int dir, ph;
                        K pk = p.key; // 获取头节点的key
                        if ((ph = p.hash) > h) // 头节点的hash 与 当前hash进行比较,
                            dir = -1; // 当前比头节点小 -1
                        else if (ph < h)
                            dir = 1; // 当前比头节点大 1
                        else if ((kc == null && (kc = comparableClassFor(k)) == null) || // 返回不为null
                                 (dir = compareComparables(kc, k, pk)) == 0) // 返回为0
                            dir = tieBreakOrder(k, pk); // 进行判断并设置dir值 对两个key对象的hashcode值进行对比 获取dir

                        TreeNode<K,V> xp = p; 
                        // 如果dir小于0 则数据添加到左子树 输入dir大于0 数据添加到右子树 并且p.left || p.right = null
                        if ((p = (dir <= 0) ? p.left : p.right) == null) {
                            x.parent = xp; // 进行设置当前节点
                            if (dir <= 0)
                                xp.left = x;
                            else
                                xp.right = x;
                            root = balanceInsertion(root, x); // 插入数据之后进行平衡左右红黑点数作出处理
                            break;
                        }
                    }
                }
            }
            moveRootToFront(tab, root); // 将获取到的root树结构节点放入到table集合中
        }
comparableClassFor(k) // key的Class是否是Comparable的实现类
    static Class<?> comparableClassFor(Object x) {
        if (x instanceof Comparable) { // String类型是Comparable的实现类
            Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
            if ((c = x.getClass()) == String.class) // bypass checks
                return c; // 返回String.class
            if ((ts = c.getGenericInterfaces()) != null) { // 以Type的形式返回本类直接实现的接口.这样就包含了泛型参数信息
                for (int i = 0; i < ts.length; ++i) { // 遍历类实现的接口
                	// 1. 需要时参数类型
                	// 2. 参数类型的真实类为 Comparable
                	// 3. 获取参数类型真实类参数集合
                	// 4. 集合长度为1
                	// 5. 集合的唯一元素是x.getClass类型 
                    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; // 返回当前类的类型。否则返回null
                }
            }
        }
        return null;
    }
compareComparables(kc, k, pk) // 因为kc == null 所以返回 0
    static int compareComparables(Class<?> kc, Object k, Object x) {
    	// 对比 x = root.key != null 
    	// x.getClass = String.Class  kc = null
        return (x == null || x.getClass() != kc ? 0 :
                ((Comparable)k).compareTo(x)); // 开始对比 root.key 和 当前节点 x.key
    }
dir = tieBreakOrder(k, pk); k = x.key pk = root.key
        static int tieBreakOrder(Object a, Object b) {
            int d;
            // a != null || b != null || 并且a和b的Class是属于一致的
            if (a == null || b == null || (d = a.getClass().getName().compareTo(b.getClass().getName())) == 0)
                d = (System.identityHashCode(a) <= System.identityHashCode(b) ? -1 : 1);// 获取两个对象原本的hash码 进行大小的比对
            return d;
        }
moveRootToFront(tab, root); tab = newTab root = 树结构
        static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
            int n;
            if (root != null && tab != null && (n = tab.length) > 0) {
                int index = (n - 1) & root.hash; // length - 1 对 root.key hash值进行hash碰撞的到存放新集合的下标位置 
                TreeNode<K,V> first = (TreeNode<K,V>)tab[index]; // 获取到当前下标的头节点
                if (root != first) { // 判断root与first不等 即进行下面操作
                    Node<K,V> rn;
                    tab[index] = root; // 将root赋值到对象下标的集合中
                    TreeNode<K,V> rp = root.prev; // 获取到root.prev节点
                    if ((rn = root.next) != null) // 如果root.next != null
                        ((TreeNode<K,V>)rn).prev = rp; // rn.prev = root.prev
                    if (rp != null) // 正常情况root是根节点 prev == null  rp == null 
                        rp.next = rn;
                    if (first != null) // first != null
                        first.prev = root; 
                    root.next = first;
                    root.prev = null;
                }
                /*
                * 如果为true,则程序继续执行。
                * 如果为false,则程序抛出AssertionError,并终止执行。
                */
                assert checkInvariants(root); // assert断言 检查红黑树
            }
        }
assert checkInvariants(root); // assert断言 检查红黑树
        static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
            TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
                tb = t.prev, tn = (TreeNode<K,V>)t.next;
            if (tb != null && tb.next != t)
                return false;
            if (tn != null && tn.prev != t)
                return false;
            if (tp != null && t != tp.left && t != tp.right)
                return false;
            if (tl != null && (tl.parent != t || tl.hash > t.hash))
                return false;
            if (tr != null && (tr.parent != t || tr.hash < t.hash))
                return false;
            if (t.red && tl != null && tl.red && tr != null && tr.red)
                return false;
            if (tl != null && !checkInvariants(tl))
                return false;
            if (tr != null && !checkInvariants(tr))
                return false;
            return true;
        }
newNode(hash, key, value, null); // 新建一个链表节点
    Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
        return new Node<>(hash, key, value, next); // 新建一个节点,并将next置空
    }
((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); // 添加一个树节点
        final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab, int h, K k, V v) {
            Class<?> kc = null;
            boolean searched = false;
            TreeNode<K,V> root = (parent != null) ? root() : this; // 获取当前树结构的根节点
            for (TreeNode<K,V> p = root;;) {
                int dir, ph; K pk;
                if ((ph = p.hash) > h) // hash值与根节点hash做对比 小于则 -1
                    dir = -1;
                else if (ph < h) // 大于为 1
                    dir = 1;
                else if ((pk = p.key) == k || (k != null && k.equals(pk))) // 等于 并且 key相等 直接返回当前节点
                    return p;
                else if ((kc == null &&
                          (kc = comparableClassFor(k)) == null) ||
                         (dir = compareComparables(kc, k, pk)) == 0) { // 等于 但是key不相等
                    if (!searched) { // searched 进入方法时候赋值为false  进入一次
                        TreeNode<K,V> q, ch;
                        searched = true;
                        if (((ch = p.left) != null && // 根节点的左边不为空 并且 可以找到当前一样的key值节点
                             (q = ch.find(h, k, kc)) != null) ||
                            ((ch = p.right) != null && // 根节点的右边不为空 并且 可以找到当前一样的key值节点
                             (q = ch.find(h, k, kc)) != null)) 
                            return q; // 将当前找到的节点进行返回
                    }
                    dir = tieBreakOrder(k, pk); // 对比两个key值对应的hash值大小 来返回1 还是-1 pk大 -1 k大 1
                }
                TreeNode<K,V> xp = p; // 拿到根节点
                if ((p = (dir <= 0) ? p.left : p.right) == null) { // 判断数据是放入左边还是放入右边 并且为null 才会进行处理
                    Node<K,V> xpn = xp.next;
                    TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn); // 创建一个新的树节点TreeNode
                    if (dir <= 0) // 小于等于 0 放入左边
                        xp.left = x;
                    else // 否则放入右边
                        xp.right = x;
                    xp.next = x; // 将x放入到next中
                    x.parent = x.prev = xp; // 当前新建的父级为root
                    if (xpn != null)
                        ((TreeNode<K,V>)xpn).prev = x;
                    moveRootToFront(tab, balanceInsertion(root, x)); // 平衡红黑树 并且进行赋值
                    return null;
                }
            }
        }
root() // 根节点的获取
        final TreeNode<K,V> root() {
            for (TreeNode<K,V> r = this, p;;) {
                if ((p = r.parent) == null) // 如果当前节点的prev == null 表示为根节点
                    return r;
                r = p; // 向上进行遍历
            }
        }
ch.find(h, k, kc) 查找当前所有树节点中是否有与key值一致的树节点,有 返回当前节点 无 返回null
        final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
            TreeNode<K,V> p = this;
            do {
                int ph, dir; K pk;
                TreeNode<K,V> pl = p.left, pr = p.right, q;
                if ((ph = p.hash) > h) // p.hash值比h大 将p左边赋值给p 
                    p = pl;
                else if (ph < h) // p.hash值比h小 将p右边赋值给p 
                    p = pr; 
                else if ((pk = p.key) == k || (k != null && k.equals(pk))) // 如果key一致 则返回当前节点
                    return p;
                else if (pl == null) // 左边为null 查找右边
                    p = pr;
                else if (pr == null) // 右边没有查找左边
                    p = pl;
                else if ((kc != null ||
                          (kc = comparableClassFor(k)) != null) &&
                         (dir = compareComparables(kc, k, pk)) != 0)
                    p = (dir < 0) ? pl : pr; // 对比实际的hash值 判断是遍历左边子树 还是遍历右边子树
                else if ((q = pr.find(h, k, kc)) != null) // 递归进行查找
                    return q;
                else
                    p = pl;
            } while (p != null);
            return null; // 上面遍历完毕没有找到目标key对应的节点 就返回null
        }
treeifyBin(tab, hash); // 将链表结构转为树结构存储数据
    final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        // static final int MIN_TREEIFY_CAPACITY = 64;
        // 如果集合 = null 或者 集合长度小于 64 进行数据容量扩充
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize(); // 进行扩容处理 
        else if ((e = tab[index = (n - 1) & hash]) != null) { // table.length > 64
            TreeNode<K,V> hd = null, tl = null;
            do {
                TreeNode<K,V> p = replacementTreeNode(e, null); // 先进行创建一个TreeNode节点
                if (tl == null)
                    hd = p; // 将节点放入到头节点的位置
                else {
                    p.prev = tl;
                    tl.next = p; // 持续相里面添加数据
                }
                tl = p;
            } while ((e = e.next) != null);
            if ((tab[index] = hd) != null)
                hd.treeify(tab); // 将hd对应的所有节点进行树结构化,并将当前树结构放入到key对应的下标中
        }
    }
replacementTreeNode(e, null); // 创建一个TreeNode节点
    TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
        return new TreeNode<>(p.hash, p.key, p.value, next);
    }

读取数据 map.get(“username”);

    public V get(Object key) {
        Node<K,V> e;
        // 获取当前节点,判断节点是否为null 为null即返回null,buweinull返回获取到的节点e
        return (e = getNode(hash(key), key)) == null ? null : e.value; 
    }
getNode(hash(key), key)根据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) { // 获取到key对应hash值 & 集合length - 1 得到key应当保存的下标位置
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k)))) // 判断当前集合下标 头节点的key是否一致
                return first; // 一致则进行返回
            if ((e = first.next) != null) { // 头节点不一致,进行向下遍历
                if (first instanceof TreeNode) // 树节点类型进行遍历
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key); // 拿到对应key的节点进行返回
                do { // 链表结构进行遍历
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k)))) // 获取到与key值一致
                        return e; // 返回
                } while ((e = e.next) != null);
            }
        }
        return null; // 如果没有找到对应的key值 即返回null
    }
((TreeNode<K,V>)first).getTreeNode(hash, key);
        final TreeNode<K,V> getTreeNode(int h, Object k) { 
        	// 如果当前first.parent != null, 获取根节点
        	// 如果当前first.parent == null, 直接赋值为this
        	// 开始查找当前树结构的数据信息 find
            return ((parent != null) ? root() : this).find(h, k, null);
        }

总结

  1. 创建
  • 不传初始容量和加载因子,系统会使用默认的 初始容量 16 和 加载因子 0.75
  • 传入初始容量非2的幂,系统会进行调整到2的幂,并且不能大于最大容量 1 << 30
  • new HashMap()对象的时候并没有去初始化集合对象table
  1. 添加
  • 集合中存在与当前添加key值一致的情况,进行替换
  • 集合不存在与当前添加key值一致的情况,进行添加. size++,modCount++
  • 添加操作会进行判断当前链表长度是否大于等于8, >= 8 && table.length >= 64, 链表结构进行树结构化. >= 8 && table.length < 64,table集合进行扩容处理.
  • key值可以为null但是只能有一个数据key为null,放在table下标为0的位置
  1. 扩容
  • 当table为null时,判断threshold是否为null,否 将threshold为table的length. 是 将默认初始容量进行赋值 16
  • 当table不为null时,判断length是否大于等于最大容量, 是 设置为Integer.MAX_VALUE; 否 在之前的基础上进行右移一位,也就是原来的一倍.
  1. 获取
  • 获取key的hash值,并对table.length-1做hash碰撞的到需要访问的table下标.进行节点的遍历,获取成功即返回value值,获取失败,返回null.
  1. 安全问题
  • 非线程安全,多个线程操作,会出现数据混乱的情况.

HashTable与HashMap的区别

初始化
HashTable : 未传初始容量和加载因子 默认是 11 0.75 最大table.length = Integer.MAX_VALUE - 8 + 1;
HashMap : 未没有传初始容量和加载因子 默认是 16 0.75 最大table.length = Integer.MAX_VALUE;
HashTable : 传初始容量 初始容量传入多少初始化就是多少长度 new HashTable()时初始化了table集合对象
HashMap : 传初始容量 会进行是否是2的幂进行判断,然后返回一个2的幂数据. new HashMap未初始化table集合对象

父类不同
HashTable : Dictionary
HashMap : AbstractMap

方法
HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。

添加数据
HashTable : value 不允许为null key 不允许为null
HashMap : value 允许为null key 允许为null (但有且仅有一个数据)

hash碰撞方式不一样
HashTable : (hash & 0x7FFFFFFF) % tab.length;
HashMap : (h = key.hashCode()) ^ (h >>> 16) & (tab.length-1)

数据结构不一样
HashTable : 数组[] + 链表
HashMap : 数组[] + 链表 \ 红黑树

扩容
HashTable : 在原来的基础上增加一倍再加1。 oldCap << 1 + 1
HashMap : 在原来的基础上增加一倍 oldCap << 1

线程安全
HashTable : 线程安全 使用关键字synchronized控制 所以效率会相对来说比较低
HashMap : 线程不安全

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值