集合源码分析,我不信看完还有人不懂(二)

LinkedHashSet源码

它是HashSet的子类,底层结构是LinkedHashMap,底层维护数组和双向链表
韩老师的讲解图
在这里插入图片描述
双向链表的构建:从AA指向456,456又指回来,然后456重复不添加,再又指向 刘 ,刘 又指回来。。。。。。这就是底层双向链表的指向

Hashmap底层放的节点是Node
LinkedHashMap底层存放的节点为Entry
在这里插入图片描述
Entry类型:
在这里插入图片描述
继承了HashMap.Node<K,V>类型

添加操作

其实就是hashmap的添加,判断hash,再重复则不添加,不重复则跟到数组位置本身的链表后面 用next指向要添加的节点

看看具体节点的属性:
在这里插入图片描述
before:表示指向双向链表的前一个节点
after:表示指向双向链表的后一个节点
hash:当前节点的hash值
key:因为是linkedhashset 所以是添加的数据
value:hashset底层默认的一个Object数据
next:表示指向当前处于同一个数组位置上的后一个节点,

其实在数组的某个位置上的一串节点,还是单向链表,用next链接
所有的节点是双向链表,用before和after链接

不要搞错了next和before、After,next是用于维护HashMap指定table位置上连接的Entry的顺序的,before、After是用于维护Entry插入的先后顺序的。

TreeSet与TreeMap一起放到后面分析

HashMap源码

在这里插入图片描述
疑问:为什么通常用String类作为key
因为String已经重写了hashcode和equals方法,我们自己新建类的话,还得自己重写 。加上String类确实常用。
而且string类的tostring方法默认返回自己,能够显示出来

HashMap的遍历

为了方便程序员去便利hashmap,我们还创建了EntrySet这个类,来专门放每个Node节点的引用,方便便利,本质上存放的还是Node类型,只不过可以放进去是因为Node这个类实现了Map.Entry这个接口,父类引用指向子类对象嘛。
在这里插入图片描述

还有Keyset这个类,专门存放每个节点的key
values这个类,专门存放每个节点的value

可以通过map.getEntrySet得到EntrySet 然后可以通过增强for或者使用迭代器进行遍历

可以通过map.getKeySet得到KeySet 然后可以通过增强for或者使用迭代器进行遍历

可以通过map.getValues 得到Values 然后可以通过增强for或者使用迭代器进行遍历

这就6种遍历方式了,不想写代码了,自己在网上看看。
HashMap源码在我之前的博客将HashSet就讲过了,可以自己去看!!

HashTable源码

在这里插入图片描述
它是线程安全的,是因为底层方法被synchronized修饰了

HashTable底层记录数据的类是

private static class Entry<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Entry<K,V> next;

        protected Entry(int hash, K key, V value, Entry<K,V> next) {
            this.hash = hash;
            this.key =  key;
            this.value = value;
            this.next = next;
        }

        @SuppressWarnings("unchecked")
        protected Object clone() {
            return new Entry<>(hash, key, value,
                                  (next==null ? null : (Entry<K,V>) next.clone()));
        }

        // Map.Entry Ops

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        public V setValue(V value) {
            if (value == null)
                throw new NullPointerException();

            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;

            return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
               (value==null ? e.getValue()==null : value.equals(e.getValue()));
        }

        public int hashCode() {
            return hash ^ Objects.hashCode(value);
        }

        public String toString() {
            return key.toString()+"="+value.toString();
        }
    }

HashTable初始化的数组,是一个HashTable$Entry类型的数组也就是下面显示的这个
在这里插入图片描述
注意:底层第一次添加数据后。数组大小为设置11(hashmap设置为16)
在这里插入图片描述
可以看见它的阈值是8(11*0.75),加载因子为0.75(hashmap也是0.75)

HashTable的数组大小默认扩容是按2倍+1扩容的
在这里插入图片描述
就是这句显示扩容大小为多少

测试,debug一下

Hashtable hashtable = new Hashtable();
        hashtable.put('1',"数据1");
        hashtable.put('2',"数据2");
        hashtable.put('3',"数据3");
        for (int i = 0; i < 10; i++) {
            hashtable.put(i+"","数据"+i);
        }
        System.out.println(hashtable);

在这里插入图片描述
添加数据到第八个时候,数组大小为11
在这里插入图片描述
添加数据到第9个时候,数组大小为23(11*2+1)

行了,大差不差的,很容易理解,HashTable就到这儿!

TreeSet(底层是TreeMap)

TreeSet是有顺序的,但是和LinkedHashMap和LinkedHashSet那些不一样
LinkedHashMap和LinkedHashSet说的有序指的是输出的顺序和插入的顺序一致
TreeSet是有顺序的,指的是插入的数据从小到大进行排序,比如随即插入1~10,输出结果从1排到10

使用无参构造,创建TreeSet的时候,默认是自然顺序排序
在这里插入图片描述

使用有参构造,创建TreeSet的时候,可以传入一个比较器,专门用来指定排列顺序的
在这里插入图片描述

为什么无参构造的时候有一个自然排序呢?

当我们第一次put,往里面放数据的时候
执行这个方法
在这里插入图片描述
注意这里!!!
在这里插入图片描述
这里要走到compare方法,如下:
在这里插入图片描述
无参构造的话,没有传入比较器,那么comparator==null就为空
就执行这个语句((Comparable<? super K>)k1).compareTo((K)k2)

也就是调用你传入的key的默认比较器,例如:我们传入一个String类型的数据,它已经实现了比较器接口
在这里插入图片描述
也对比较方法进行了重写
在这里插入图片描述
因此!!!!!!!!!!!!!
key为String类型的时候,就默认调用String类型的比较器所以在我们看来没有传入比较器,但是他还是实现了自然排序

添加
public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

有一个注意点:
添加数据,再进行比较的时候,如果发生比较失败,则数据不会添加进去,因为它直接return

TreeMap中的键,值能否为null?(要看哦)

看这个博主的
https://blog.csdn.net/u012156116/article/details/81073570

到现在,这里面源码再不考虑树的添加数据方面的源码的情况下,逻辑很简单,不再看了!!!

Collections工具类

在这里插入图片描述

怎么选择使用哪一种集合?

在这里插入图片描述

就到这儿结束吧!集合不难,很多源码我没有讲解,基本上读了一两个源码后,了解其他的就很容易了!!! 愿诸君加油!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值