Java集合——Set接口实现类的源码剖析——最认真的图解——第二章

本文深入探讨了Java中的Set接口,重点是HashSet的实现原理和特性。通过集合体系图介绍了Set与Collection的关系,详细讲解了HashSet的底层机制,包括基于HashMap的存储结构、扩容机制和红黑树转换。此外,还提供了HashSet的实战案例,展示了如何处理重复元素,以及对比分析了LinkedHashSet的插入有序特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1:集合体系图

Collection:单列集合,该接口有两个重要的子接口List Set,他们的实现子类都是单列集合
在这里插入图片描述
Map:双列集合,Map接口的实现子类,存放K-V
在这里插入图片描述

2:Set接口和常用方法

Set接口基本介绍

1)无序(添加和取出的顺序不一致)没有索引(没有get方法,所以也没有普通for循环遍历)
2) 不允许重复元素,所以最多包含一个null
3) JDK API中Set接口的实现类有:
在这里插入图片描述
Set接口的常用方法

和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样.

Set接口的遍历方式

同Collection的遍历方式一样,因为Set接口是Collection接口的子接口。
1.可以使用迭代器

2.增强for

3.不能使用索引的方式来获取.

案例演示

public class SetMethod {
    public static void main(String[] args) {
        //注意:取出的顺序虽然不是添加的顺序,但是它是固定的
        Set hashSet = new HashSet();
        hashSet.add("zlj");
        hashSet.add("zlj");
        hashSet.add("zzz");
        hashSet.add(null);
        hashSet.add(null);
        System.out.println("hashSet="+hashSet);
                System.out.println("=======Iterator遍历==========");
        Iterator iterator = hashSet.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println("hashSet="+next);
        }
        System.out.println("=======增强for循环遍历==========");

        for (Object object:hashSet){
            System.out.println("hashSet="+object);
        }
    }
}
hashSet=[null, zlj, zzz]
=======Iterator遍历==========
hashSet=null
hashSet=zlj
hashSet=zzz
=======增强for循环遍历==========
hashSet=null
hashSet=zlj
hashSet=zzz

3:HashSet全面说明

  1. HashSet实现了Set接口

  2. HashSet实际上是HashMap

    public HashSet() {
        map = new HashMap<>();
    }

3)可以存放nul值,但是只能有一个null

  1. HashSet不保证元素是有序的,取决于hash后,再确定索引的结果。(即不保证存放元素的顺序,和取出的顺序一致)
    5)不能有重复元素/对象在前面Set接口使用已经讲过

我们先来理解一下什么是重复元素/对象,看如下的例子

public class HashSet01 {
    public static void main(String[] args) {

        Set set=new HashSet();
        System.out.println(set.add(1));//添加成功
        System.out.println(set.add(1));//添加失败

        System.out.println(set.add(new Dog("zzz")));//添加成功
        System.out.println(set.add(new Dog("zzz")));//添加成功

        //经典面试题,为什么第二个new String就是添加失败,我们就必须要去看它的源码
        System.out.println(set.add(new String("zzz")));//添加成功
        System.out.println(set.add(new String("zzz")));//添加失败
    }
}
true
false
true
true
true
false

为什么第二个new String就是添加失败?到底什么叫做重复元素/对象呢?这就需要我们去观察HashSet的源码,来进行分析

3.1 HashSet底层机制说明

HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树)
在这里插入图片描述
我们现在就使用Java模拟一下HashSet底层,数组+链表,模拟上面的图

public class HashSetStructure {
    public static void main(String[] args) {
        //模拟一个HashSet的底层(HashMap底层结构)
        //1:创建一个数组,数组的类型是Node[]
        //2:有些人,直接把Node[]数组称为表
        Node[] nodes = new Node[16];
        System.out.println("table="+nodes);
        //3:创建节点john存入到 nodes[2]
        Node john = new Node("john", null);
        nodes[2]=john;
        Node jack = new Node("jack", null);
        john.next=jack;//将jack节点挂载到john
        Node rose = new Node("Rose", null);
        jack.next=rose;//将rose节点挂载到jack
         //3:创建节点lucy 存入到 nodes[3]
        Node lucy = new Node("Lucy", null);
        nodes[3]=lucy;
        System.out.println("table="+nodes);
    }
}
class Node{//节点,存储数据,可以指向下一个节点,从而形成链表
    Object item;//存放数据
    Node next;//指向下一个节点
    public Node(Object item, Node next) {
        this.item = item;
        this.next = next;
    }
}

使用debug,john成功放在数组2中,Lucuy成功放在数组3中,并且john后存放jack,Rose
在这里插入图片描述

分析HashSet的添加元素底层是如何实现(hash()+equals())
在这里插入图片描述
源码解读

public class HashSetSource {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add("java");//第一个方法
        hashSet.add("php");//第二个方法
        hashSet.add("java");//第三个方法
        System.out.println("set="+hashSet);
    }
}

在这里插入图片描述

3.2 HashSet底层机制——扩容和转成红黑树机制再说明

先说结论,在进行代码演示
在这里插入图片描述
在这里插入图片描述

演示HashSet的扩容机制

public class HashSetIncrement {
    public static void main(String[] args) {
/**
 * HashSet底层是HashMap,第一次添加时, table数组扩容到16,
 * 临界值(threshoLd)是16* 加载因子(LoadFactor)是0.75 = 12
 * 如果table数组使用到了临界值12,就会扩容到16 * 2 = 32,
 * 新的临界值就是32*0.75 = 24,依次类推
 */
        HashSet hashSet=new HashSet();
        for (int i=1;i<100;i++){
            hashSet.add(i);
        }
    }
}

在这里插入图片描述
演示HashSet的转成红黑树机制
代码演示

public class HashSetIncrement {
    public static void main(String[] args) {
/**

 */
        HashSet hashSet=new HashSet();
        for (int i=1;i<100;i++){
//            hashSet.add(i);
            hashSet.add(new A(i));
        }
                System.out.println("Hashset="+hashSet);
    }
}
class  A{
    private int i;
    public A(int i) {
        this.i = i;
    }
    @Override//为了保证hash一样,但是键不一样,就可以放在同一个数组索引中的不同链表上去
    public int hashCode(){
        return 100;
    }
}

在这里插入图片描述

4:HashSet实战

定义一个Employee类,该类包含: private成员属性name,age
要求:
1.创建3个Employee对象放入HashSet中
2.当name和age的值相同时,认为是相同员工,不能添加到HashSet集合中
代码演示

public class HashSetExercise {
    public static void main(String[] args) {
        Set set=new HashSet();
        set.add(new Employee("zlj",22));
        set.add(new Employee("zlj",22));
        set.add(new Employee("zl",21));
        System.out.println("Hashset="+set);
    }
}
class Employee{
    private String name;
    private int age;
    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //重写equals和hashCode,年龄,名字都相同,比较hash值,如果相同,再比较对象的内容,对象内容相同,就返回
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return age == employee.age &&
                Objects.equals(name, employee.name);
    }
    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }
    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
Hashset=[Employee{name='zl', age=21}, Employee{name='zlj', age=22}]

5:LinkedHashSet介绍

在这里插入图片描述

  1. LinkedHashSet是HashSet的子类

  2. LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表
    在这里插入图片描述

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

  4. LinkedHashSet不允许添重复元素

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值