集合_Collection_LinkedHashSet简述

先说结论:

LinkedHashSet与HashSet的区别,就是LinkedHashSet存在有序性,即数据存入的顺序与取出的顺序一致

LinkedHashSet是HashSet的子类,其构造方法内部均调用其父类HashSet的构造方法;而被调用的HashSet的构造方法内部调用LinkedHashMap的构造方法。所以LinkedHashSet的底层,即为LinkedHashMap

LinkedHashSet中数据的存储方式为:数组 + 单向链表 + 双向链表

数组:指table数组,继承自HashMap,类型为HashMap$Node,存放的数据类型为LinkedHashSet$Entry

单项链表:数组中,一个索引处存在一个单向链表;next属性指向下一结点

双向链表:构成一个个单向链表的结点,一同构成的双向链表;before属性指向前一个被添加进来的元素,after指向后一个被添加进来的结点;LinkedHashSet中存在两个属性:head与tail,head指向双向链表的头结点,tail指向双向链表的尾结点。正是该双向链表,实现了LinkedHashSet的有序性

文末有对于数组 + 单向链表 + 双向链表模式的比喻 

    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

关于Entry与Node: 

        LinkedHashMap中存在静态内部类Entry,其内部维护的数组table中存放的数据类型即为Entry;数组table继承自其父类HashMap,类型为HashMap$Node,而之所以table数组能存放Entry类型的数据,是因为Entry继承了Node

        Entry中除了继承自其父类Node的属性和方法外,自身拥有两个属性:before与after。其中,before指向上一个被添加进来的元素,after指向下一个被添加进来的元素

    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }

关于head与tail及双向链表的链接:

        LinkedHashMap中,存在两个属性:head与tail,其中head指向最先被添加进来的元素,tail指向最后被添加进来的元素

    transient LinkedHashMap.Entry<K,V> head;

    transient LinkedHashMap.Entry<K,V> tail;

        双向链表的链接,是通过linkNodeLast方法实现的

        进入方法后,先创建局部变量last,并将tail的引用赋值给last,此时last即为旧尾结点

        而后将新尾结点p的引用赋值给tail,完成了tail的更新

        接下来进行判断,新尾结点p是table数组中的第一个元素时,将head与tail一同指向p,否则进行链表链接:将新尾结点p的before属性指向旧尾结点last,将旧尾结点last的after属性指向新尾结点p

    private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail;
        tail = p;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
    }

        LinkedHashMap中重写了newNode方法,让该方法在返回一个新建结点的同时,调用linkNodeLast方法(newNode方法在HashMap的putVal方法中被调用,putVal方法在put方法中被调用,put方法在HashSet的add方法中被调用)

    Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<K,V>(hash, key, value, e);
        linkNodeLast(p);
        return p;
    }

        此时有趣的现象出现了,数组的一个索引对应着一条单向链表(若该索引处存储了元素的话),而组成一条条单向链表的结点,一同组成了一条双向链表,正是这条双向链表让LinkedHashSet存在有序性。

关于数组 + 单向链表 + 双向链表的比喻:

        一批求职者(元素)来到工厂(集合)面试(尝试向集合中添加元素),工厂根据求职者的求职意向(hash)将他们分配到不同的车间(索引)面试。面试合格者(准备添加的元素与索引对应的单向链表中的元素不重复)将被招入工厂,并分配工号(链接双向链表)与车间工号(链接单向链表),车间工号分配到9时,车间将会升级为大车间(树化)。工厂有时会进行点名(输出集合中的元素),点名的顺序依工号而定

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值