Java集合(七)LinkedHashSet

LinkedHashSet的全面说明:

(1)LinkedHashSet是HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。对于频繁的遍历操作,LinkedHashSet效率要高于HashSet.

(2)LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表

HashSet为数组+单向链表

(3)LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。

(4)LinkedHashSet不允许添重复元素。

我们进入源码进行查看:

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{

LinkedHashSet底层机制说明:

(1)在LinkedHashSet中维护了一个hash表和双向链表(LinkedHashSet有head(头)和tail(尾))

(2)每一个结点有before和after属性,这样可以形成双向链表

(3)在添加一个元素时,先求hash值,在求索引,确定该元素在table的位置,然后将添加的元素加入到双向链表(如果已经存在,不添加【原则和hashset一样】)

tail.next=newElement  //示意代码

newElement=tail

tail=newElement;

(4)这样的话,我们遍历LinkedHashSet也能确保插入顺序和遍历顺序一致。

我们设计的代码如下所示:

package com.rgf.set;

import java.util.LinkedHashSet;
import java.util.Set;

@SuppressWarnings({"all"})
public class LinkedHashSetSource {
    public static void main(String[] args) {
        //分析一下LinkedHashSet的底层机制
        Set set = new LinkedHashSet();
        set.add(new String("AA"));
        set.add(456);
        set.add(456);
        set.add(new Customer("刘",1001));
        set.add(123);
        set.add("HSP");
        System.out.println("set="+set);
    }
}
class Customer{
    private String name;
    private int no;

    public Customer(String name, int no) {
        this.name = name;
        this.no = no;
    }
}

我们运行之后如下所示:

 我们根据运行结果,我们发现:LinkedHashSet加入顺序和取出元素/数据的顺序一致

之后我们根据Debug来进行查看:

我们发现创建了table表初始化仍然为16个,同时底层发生了变化,table表的元素不再是原先的Node类型了,而是Entry这样子一个对象。添加第一次时,直接将数组table扩容到16,存放的结点类型是LinkedHashMap$Entry,table是HashMap$Node内部类数组的这样子一个类型,但是他里面存放的真实的元素是LinkedHashMap$Entry这种类型,他们之间有继承或者实现关系,Entry继承或者实现了Node。数组是HashMap$Node[],存放的元素/数据是LinkedHashMap$Entry

多态就是父类引用指向子类对象,多态数组就是存放的数据类型是数组类型的子类

我们来进行查看LinkedHashMap的源码来进行了解:

我们进入源码界面后,找到Structure,发现了entey方法:

//Entry是LinkedHashMap里面的静态内部类,继承了HashMap.Node
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);
        }
    }

我们查看完源码后,继续进行查看:

 我们进入HashMap源码:

 我们找到Node类:

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

为静态内部类,底层源码才可以用HashMap.Node

我们添加元素的过程如下所示:

 

 这样子即形成了一个双向链表。

我们还可以进行查看head和tail。 

我们的代码如下所示:

package com.rgf.set;

import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

@SuppressWarnings({"all"})
public class LinkedHashSetSource {
    public static void main(String[] args) {
        //分析一下LinkedHashSet的底层机制
        Set set = new LinkedHashSet();
        set.add(new String("AA"));
        set.add(456);
        set.add(456);
        set.add(new Customer("刘",1001));
        set.add(new Customer("任",1002));
        set.add(123);
        set.add(new Customer("杨",1003));
        set.add("HSP");
        System.out.println("set="+set);
        //1.LinkedHashSet加入顺序和取出元素/数据的顺序一致
        //2.LinkedHashSet底层维护的是一个LinkedHashMap
        //3.LinkedHashSet 底层结构(数组+双向链表)
        //4.添加第一次时,直接将数组table扩容到16,存放的结点类型是LinkedHashMap$Entry
        //table是HashMap$Node内部类数组的这样子一个类型,但是他里面存放的真实的元素是LinkedHashMap$Entry这种类型
        //他们之间有继承或者实现关系,Entry继承或者实现了Node.
        //5.数组是HashMap$Node[],存放的元素/数据是LinkedHashMap$Entry,
        //继承关系是在内部类完成的

    }
}
class Customer{
    private String name;
    private int no;

    public Customer(String name, int no) {
        this.name = name;
        this.no = no;
    }
}

实例练习:

Car类(属性:name,price),如果name和price一样,则认为是相同元素,就不能添加。

我们设计的代码如下所示:

package com.rgf.set;

import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;

@SuppressWarnings({"all"})
public class LinkedHashSetExercise {
    public static void main(String[] args) {
        Set set = new LinkedHashSet();
        set.add(new Car("奥拓",1000));
        set.add(new Car("奥迪",30000));
        set.add(new Car("法拉利",10000000));
        set.add(new Car("奥迪",30000));
        set.add(new Car("保时捷",70000000));
        set.add(new Car("奥迪",30000));
        System.out.println("set="+set);
    }
}
class Car{
    private String name;
    private double price;

    public Car(String name, double price) {
        this.name = name;
        this.price = price;
    }
//重写equals方法和hashCode
    //当name和price相同时,就返回相同的hashCode值,equals返回t
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }

    @Override
    public String toString() {
        return "\nCar{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

运行界面如下所示:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直再追梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值