【10.26】Java源码学习

HashMap方法实现简单解析

treeifyBin方法
  1. 功能:使用树节点替换当前哈希对应索引下标的哈希桶中的所有节点,除非表的大小太小,需要是用扩容方法替代树化节点方法
  2. 传入的参数:当前的节点数组,需要树化的桶的哈希值
final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;  // 生命工作指针--整型的数字和下标--节点的引用工作指针
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)  // 如果当前的表为空或者当前表的长度小于树化的最小容量,那么就进行扩容操作
            resize();  // 调用扩容表大小的方法
        else if ((e = tab[index = (n - 1) & hash]) != null) {  // 如果需要树化的桶不为空
            TreeNode<K,V> hd = null, tl = null;		// 生命节点引用工作指针
            do {
                TreeNode<K,V> p = replacementTreeNode(e, null);  // 将头节点转化为树节点,调用replacementTreeNode方法
                if (tl == null)
                    hd = p;   // hd首先初始化为头节点
                else {
                    p.prev = tl;
                    tl.next = p;  // 遍历链表中的所有节点,并且将所有的节点转化为属性节点,并且
                }
                tl = p;  // t1首先初始化为头节点
            } while ((e = e.next) != null);
            if ((tab[index] = hd) != null)to
                hd.treeify(tab);  // 调用treeify方法将桶中的链式结构整理为属性结构
        }
    }
putAll方法
  1. 功能:使用putMapEntries方法将一个特殊的表中的映射插入到目标的表中
  2. 参数:一个需要特殊的表映射对象
remove方法
  1. 功能:调用removeNode的方法删除一个在表中存在的映射
  2. 参数:映射的key值
  3. 返回:如果存在并且删除后,返回映射的value值,否则返回Null
removeNode方法
  1. 功能:实现了remove以及相关的方法
  2. 参数:
    • hash:key的哈希值
    • key:key的对象
    • value: 映射对应的value对象(可以为Null)
    • boolean matchValue: 如果需要匹配映射对应的value对象,那么就为true
    • boolean removable: 如果为false那么就不能删除其他的节点(有一点不懂)
  3. 返回:被删除的节点
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) {   // 如果表不为空,并且长度大于0,并且目标key对应的桶不为空
            Node<K,V> node = null, e; K k; V v;		// 声明工作的节点
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))  // 如果第一个节点就是对应的key
                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)))) {	  // 如果找到了节点并且满足对value的要求(注意key不会重复)
                if (node instanceof TreeNode)
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);  // 如果为树节点,那么调用removeTreeNode方法进行删除
                else if (node == p)		// 如果为普通节点并且为头节点,那么直接使用头结点的下一个节点作为头节点即可
                    tab[index] = node.next;
                else			// 如果为普通链式节点并且不为头节点,那么进行相应的删除操作
                    p.next = node.next;
                ++modCount;		// 如果删除了,那么修改次数增加,节点数目减少
                --size;
                afterNodeRemoval(node);  // 这个是个啥
                return node;  // 找到了就返回删除了的节点
            }
        }
        return null;  // 没找到则返回Null
    }
clear方法
  1. 功能:清空表
  2. 无参数,无返回值
 public void clear() {
        Node<K,V>[] tab;  // 工作指针
        modCount++;  // 修改次数增加
        if ((tab = table) != null && size > 0) {  // 如果表结构不为空并且大小大于0
            size = 0;  // 更新大小为0
            for (int i = 0; i < tab.length; ++i)  // 遍历表结构并且设置表的每一项都为null空值
                tab[i] = null;
        }
    }
containsValue方法
  1. 功能:如果表中存在一个或者多个某个查询的值,那么返回真
  2. 参数:需要查询的value对象
  3. 返回:boolean值
 public boolean containsValue(Object value) {
        Node<K,V>[] tab; V v;			// 工作指针--表,值对象
        if ((tab = table) != null && size > 0) {  // 如果表已经初始化并且大小不为空
            for (Node<K,V> e : tab) {	// 遍历节点数组中的桶
                for (; e != null; e = e.next) {  // 遍历桶中的每一个节点【好随意,怎么都不看看是那种类型的节点呢,树能这么遍历?】
                    if ((v = e.value) == value ||
                        (value != null && value.equals(v)))  // 如果节点的Value等于目标Value对象那么返回true
                        return true;
                }
            }
        }
        return false;  // 没找到怎返回false
    }
keySet方法
  1. 功能:返回keySet内部类
  2. 参数:无
  3. 返回值:Set
  4. 返回对于映射表的键对象的集合形式。这个加对象集合被这个映射对象维护,因此如果改变这个映射就会影响这个集合对象,反过来改变这个集合也会影响这个映射对象。
  5. 当这个映射表对象在被一个迭代器遍历访问时,如果非当前迭代器对此键对象集合进行修改,那么就会导致遍历发生错误
  6. 这个键对象集合支持删除操作,并且能够删除键对应的映射对,但不支持add/addAll操作
 public Set<K> keySet() {
        Set<K> ks = keySet;  // 初始化一个应用指针
        if (ks == null) {  // 如果键对象集合为空,那么就进行初始化
            ks = new KeySet();
            keySet = ks;
        }
        return ks;  返回键对象集合
    }
prepareArray方法
  1. 功能:实现一个函数式编程的函数接口,准备一个适合大小的数组来将表转化的数组
  2. 参数:一个预备的数组
  3. 返回值:一个大小适当的预备数组
 @SuppressWarnings("unchecked")   // 不知道这个啥意思
    final <T> T[] prepareArray(T[] a) {
        int size = this.size;   // 获取当前表中映射对的数目
        if (a.length < size) {   // 如果于备数组笑了,那么就返回一个大小合适并且类型一致的数组
            return (T[]) java.lang.reflect.Array
                    .newInstance(a.getClass().getComponentType(), size);
        }
        if (a.length > size) {   // 如果大小大了,那么将size处写为null
            a[size] = null;
        }
        return a;
    }
keysToArray
  1. 功能:将映射表中的所有键对象组合为一个数组,并且返回。首先这个方法会确保传入的数组大小足够(调用prepareArray方法来确保)
  2. 参数:一个预备装载的数组
  3. 返回:一个使用所有键对象填充的数组
<T> T[] keysToArray(T[] a) {
        Object[] r = a;    // 【有点不明白为什么不定义为T[]】
        Node<K,V>[] tab;  // 工作表引用
        int idx = 0;  // 工作指针表示下标
        if (size > 0 && (tab = table) != null) {  
            for (Node<K,V> e : tab) {   // 遍历每一个桶
                for (; e != null; e = e.next) {   // 遍历每一个桶中的每一个节点【树结点咋整】
                    r[idx++] = e.key;  // 进行key对象得获取,看得出来就是一个浅拷贝哈
                }
            }
        }
        return a;  // 返回这个数组
    }
valuesToArray
  1. 功能:将映射表中得所有映射得value对象组成一个数组并且返回。这个方法通过调用prepareArray方法确保数组足够大
  2. 参数:预备数组
  3. 返回:填充好的数组
  4. 代码和keysToValue类似,都是浅拷贝
内部类KeySet
  1. 继承了AbstractSet类型
  2. 获取大小方法,直接返回映射表中保存得映射数目
  3. 清除方法,同时会调用当前映射表的清除方法
  4. 遍历方法会产生一个KeyIterator对象
  5. 查找方法会调用映射表对象的containsKey方法
  6. 删除方法会调用映射表对象的removeNode方法
  7. toArray方法,调用了keysToArray方法,prepareArray方法
  8. forEach方法使用了函数式编程,可以传入一个Consumer(运用Consumer.accept方法);并且在forEach遍历过程前后,会检查修改版本号是否一致,否则就发生快速失败,抛出异常
 public <T> T[] toArray(T[] a) {
            return keysToArray(prepareArray(a));  // 调用了prepareArray确保数组大小足够
        }

        public final void forEach(Consumer<? super K> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();   // 如果forEach没有传入的参数Consumer则抛出异常[Consumer对象一般用于修改某个对象,可以进行函数式编程,大括号里面表示对每一个对象的修改或者操作]
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (Node<K,V> e : tab) {  //遍历每一个节点对象
                    for (; e != null; e = e.next)
                        action.accept(e.key);  // 对每一个节点对象进行处理和操作
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();  // 检查遍历过程中是否存在被其他操作结构性修改的冲突
            }
        }
values方法
  1. 功能:和keySet方法类似
Values内部类
  1. 和KeySet内部类类似,但是不支持remove方法

小结

  1. 今天继续学习了一下HashMap,目前对于树化的机制还不是很清楚,但是一些基本的操作更清晰了,还有HashMap的一些设计方案和使用的注意事项也更加清晰
  2. 看到了函数式编程和通配符的大量应用,虽然不会写但是越来越熟悉了
  3. 看到了一些设计模式,对于冲突访问的快速失败是如何实现的
  4. 还有一些内部类的实现机制
  5. 练习了英语,那个view我终于明白了它的大致意思
  6. 对树形化的处理越来越好奇了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值