Java集合——Set接口学习总结

news2024/11/29 5:39:26

一、HashSet实现类

1.常用方法

    增加:add(E e)
    删除:remove(Object o)、clear()
    修改:
    查看:iterator()
    判断:contains(Object o)、isEmpty()
	常用遍历方式:
		Set<String> set = new HashSet<String>();
        set.add("aa");
        set.add("bb");
        set.add("cc");
        //1.迭代器打印
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        //2.增强for
        for (String s : set) {
            System.out.println(s);
        }
        //3.直接输出
        System.out.println(set);

2.JDK1.8(jdk1.8.0_361)源码下(简要)

public class HashSet<E> extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    //成员变量
    private transient HashMap<E,Object> map; //HashSet存储主体
    private static final Object PRESENT = new Object();
    //构造器,可以看出来底层是用的HashMap来实现的,这块需要对HashMap源码熟悉
    //创建出来的时候,数组是null,只有调用add方法才会进行数组初始化
    //Constructs a new, empty set; the backing HashMap instance has default initial capacity (16) and load factor (0.75).
    //构造一个具有默认初始容量(16)和负载因子(0.75)的新的,空的链接散列集
    public HashSet() {
        map = new HashMap<>();
    }
    
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }
	//这个就是LinkedHashSet创建时会调用的构造器
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }
    //添加
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

    //删除
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }
    public void clear() {
        map.clear();
    }

}

3.HashSet原理图

public class TestSet {
    public static void main(String[] args) {
        HashSet<Student> hs = new HashSet<>();
        hs.add(new Student(19,"lili"));
        hs.add(new Student(20,"lulu"));
        hs.add(new Student(18,"feifei"));
        hs.add(new Student(19,"lili"));
        hs.add(new Student(10,"nana"));
        hs.add(new Student(10,"nana"));
        System.out.println(hs.size());
        //并没有出现唯一数据的情况,为什么呢?
        //原因是没有重写Student类的hashcode和equals方法,无法判断是否同一个数据
        System.out.println(hs);

        HashSet<Integer> hs01 = new HashSet<>();
        System.out.println(hs01.add(19));//true
        hs01.add(6);
        hs01.add(17);
        hs01.add(11);
        hs01.add(3);
        System.out.println(hs01.add(19));//false 这个19没有放入到集合中
        System.out.println(hs01.size());//唯一,无序
        System.out.println(hs01);

    }
}

class Student {
    int age;
    String name;

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }
}

运行结果
在这里插入图片描述
如果不了解HashMap底层的话,可以结合以下图片进行说明:
在这里插入图片描述

总结:HashSet底层是使用的HashMap类来进行的存储,因此底层存储是通过数组+链表方式实现数据存储的。
HashSet的无序、唯一是基于HashMap(key,PRESENT)来实现的,所以对于基础数据类型、String类型是无序唯一的,但是对于没有重写过hashcode方法和equals方法的引用类来说不是唯一的。这块理解需要解锁前置技能- HashMap。

二、LinkedHashSet实现类

1.常用方法

LinkedHashSet是HashSet的子类,因此方法使用跟HashSet相同

    增加:add(E e)
    删除:remove(Object o)、clear()
    修改:
    查看:iterator()
    判断:contains(Object o)、isEmpty()
	常用遍历方式:...

2.JDK1.8(jdk1.8.0_361)源码

public class LinkedHashSet<E> extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
    //调用父类HashSet的构造方法,构造一个具有默认初始容量(16)和负载因子(0.75)的新的,空的链接散列集。
    public LinkedHashSet() {
        super(16, .75f, true);
    }
    public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    }
    public LinkedHashSet(Collection<? extends E> c) {
        super(Math.max(2*c.size(), 11), .75f, true);
        addAll(c);
    }
    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }
    public Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
    }
}

总结,源码非常简短,从调用的构造方法上是可以看出,实际LinkedHashSet底层是使用的LinkedHashMap进行存储。其实就是在HashSet的基础上,多了一个总的链表,这个总链表将放入的元素串在一起,方便有序的遍历,(可以看到LinkedHashMap.Entry 继承自HashMap.Node 除了Node 本身有的几个属性外,额外增加了before after 用于指向前一个Entry 后一个Entry。也就是说,元素之间维持着一条总的链表数据结构。)。这块理解需要有有前置技能- LinkedHashMap和HashMap。

三、比较器(TreeSet理解前置技能)

【1】以int类型为案例:
比较的思路:将比较的数据做差,然后返回一个int类型的数据,将这个int类型的数值 按照 =0 >0 <0

	int a = 10;
	int b = 20;
	System.out.println(a-b); // -10 通过=0  >0  <0来判断

【2】比较String类型数据:
String类实现了Comparable接口,这个接口中有一个抽象方法compareTo,String类中重写这个方法即可

	String a = "A";
	String b = "B";
    System.out.println(a.compareTo(b)); //-1,String比较器源码如下

在这里插入图片描述

【3】比较double类型数据:

	double a = 9.6;
    double b = 9.3;
    System.out.println(((Double) a).compareTo((Double) b)); //1

在这里插入图片描述

【4】比较自定义的数据类型:
(1)内部比较器:
通过实现Comparable接口实现,缺点是只有一个比较方法,不能实现多个不同属性的比较方法


public class Student implements Comparable<Student>{
    private int age;
    private double height;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Student(int age, double height, String name) {
        this.age = age;
        this.height = height;
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", height=" + height +
                ", name='" + name + '\'' +
                '}';
    }
    @Override
    public int compareTo(Student o) {
        //按照年龄进行比较:
        /*return this.getAge() - o.getAge();*/
        //按照身高比较
        /*return ((Double)(this.getHeight())).compareTo((Double)(o.getHeight()));*/
        //按照名字比较:
        return this.getName().compareTo(o.getName());
    }
}

public class TesBJ {
    public static void main(String[] args) {
        //比较两个学生:
        Student s1 = new Student(14,160.5,"alili");
        Student s2 = new Student(14,170.5,"bnana");
        System.out.println(s1.compareTo(s2)); //结果 -1
    }
}

(2)外部比较器:

import java.util.Comparator;

public class Student{
    private int age;
    private double height;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Student(int age, double height, String name) {
        this.age = age;
        this.height = height;
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", height=" + height +
                ", name='" + name + '\'' +
                '}';
    }
}
class BiJiao01 implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        //比较年龄:
        return o1.getAge()-o2.getAge();
    }
}
class BiJiao02 implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        //比较姓名:
        return o1.getName().compareTo(o2.getName());
    }
}
class BiJiao03 implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        //在年龄相同的情况下 比较身高  年龄不同比较年龄
        if((o1.getAge()-o2.getAge())==0){
            return ((Double)(o1.getHeight())).compareTo((Double)(o2.getHeight()));
        }else{//年龄不一样
            return o1.getAge()-o2.getAge();
        }
    }
}

【5】外部比较器和内部比较器 谁好?
答案:外部比较器,多态,扩展性好

理解完比较器后就能开始看TreeSet的源码了。

四、TreeSet实现类

1.常用方法

HashSet的子类,所以常用方法同HashSet

2.TreeSet使用

【1】存入Integer类型数据:(底层利用的是内部比较器)
在这里插入图片描述
特点:唯一,无序(没有按照输入顺序进行输出), 有序(按照升序进行遍历)
底层原理:二叉树(数据结构中的一个逻辑结构)
TreeSet底层的二叉树的遍历是按照升序的结果出现的,这个升序是靠中序遍历得到的(不熟悉的话得再看一下数据结构与算法):
在这里插入图片描述

【2】放入String类型数据:(底层实现类内部比较器)
在这里插入图片描述
【4】想放入自定义的Student类型的数据:
(1)利用内部比较器:

public class Student implements Comparable<Student> {
    private int age;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
    @Override
    public int compareTo(Student o) {
    	//按年龄排序
        return this.getAge()-o.getAge();
    }
}

在这里插入图片描述
少了一个,是因为比较器按照年龄进行比较,年龄相同的去除掉。TreeSet的add方法内部调用的TreeMap的put方法,详细解析需要看TreeMap源码中put方法中如何调用的比较器,以及根据比较器返回的结果>0 =0 <0做了什么操作,外部比较器同理。
(2)通过外部比较器:

import java.util.Comparator;

public class Student{
    private int age;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

}
class BiJiao implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getName().compareTo(o2.getName());
    }
}

在这里插入图片描述

3.JDK1.8(jdk1.8.0_361)源码下(简要)

简要拿出常用部分,其余方法可以自行看jdk1.8的文档,utools软件里面有

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    //存储的数据就是在这里
    //NavigableMap<K,V>是接口,TreeMap<K,V>是这个接口的实现类
    private transient NavigableMap<E,Object> m;
    //虚拟固定的value
    private static final Object PRESENT = new Object();

    //没有传参数时,构造器就是直接new TreeMap<E,Object>()再传给下一个构造器
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }

    //没有修饰符,只能同包内使用
    TreeSet(NavigableMap<E,Object> m) {
        //接口=接口实现类,多态
        this.m = m;
    }

    //传入集合c所有元素添加进TreeSet
    public TreeSet(Collection<? extends E> c) {
        this();
        addAll(c);
    }

    //传入比较器,排序就是根据比较器规则进行处理。
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }


    //SortedSet<E>是接口,NavigableSet<E>接口继承自此接口
    //形参是接口,调用传实参是实现类,接口=接口实现类,多态
    public TreeSet(SortedSet<E> s) {
        this(s.comparator());
        addAll(s);
    }

    //升序迭代器
    public Iterator<E> iterator() {
        return m.navigableKeySet().iterator();
    }
    //降序迭代器
    public Iterator<E> descendingIterator() {
        return m.descendingKeySet().iterator();
    }
    //以降序返回一个新的 TreeSet 集合
    public NavigableSet<E> descendingSet() {
        return new TreeSet<>(m.descendingMap());
    }
    //元素个数
    public int size() {
        return m.size();
    }
    //是否为空
    public boolean isEmpty() {
        return m.isEmpty();
    }
    //是否含有元素o
    public boolean contains(Object o) {
        return m.containsKey(o);
    }
    //添加
     public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }
    //删除
    public boolean remove(Object o) {
        return m.remove(o)==PRESENT;
    }
    //清空元素
    public void clear() {
        m.clear();
    }
}

总结:TreeSet部分源码较多,简化出了常用的方法。这块的理解需要了解TreeMap源码和树结构。

五.Collection部分整体结构图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/416799.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

redis中布隆过滤器使用详解

文章目录一、布隆过滤器介绍1、什么是布隆过滤器2、布隆过滤器实现原理3、误判率4、布隆过滤器使用场景5、哈希表与布隆过滤器比较二、redis中布隆过滤器实战1.引入redisson依赖2.创建订单表3.配置redis4.配置BloomFilter5.创建订单6.单元测试总结一、布隆过滤器介绍 1、什么是…

什么是汽车以太网?

总目录链接>> AutoSAR入门和实战系列总目录 总目录链接>> AutoSAR BSW高阶配置系列总目录 文章目录什么是汽车以太网&#xff1f;汽车以太网市场中使用的标准和剖析汽车以太网类型什么是汽车以太网&#xff1f; 本页介绍了汽车以太网的基本特性并提到了汽车以…

【数据库】关系数据库

1.选择关系&#xff08;对行操作&#xff09; 2.投影&#xff08;对列操作&#xff09; &#xff08;行记录重复的不再显示&#xff09; 3.连接&#xff08;从两个关系的笛卡尔积中选出属性间满足一定条件的元组&#xff09; a.等值连接 b.自然连接&#xff08;等值连接的特殊…

【云原生Docker】08-Docker存储

【云原生|Docker】08-Docker存储 文章目录【云原生|Docker】08-Docker存储简介Docker存储挂载方式挂载方式介绍挂载主机目录数据卷容器特性Docker存储示例挂载主机目录Type: bindType: volume总结数据卷容器利用数据卷容器迁移数据备份数据&#xff1a;恢复数据&#xff1a;Doc…

【小程序】小程序组件-1

一. form组件的使用 这个点自己写的时候卡了好久&#xff0c;比较有感悟。 首先明确一点&#xff0c;为什么需要form。 form可以封装一个模块&#xff0c;这个模块里可以有多个input操作&#xff0c;多个输出接口&#xff0c;多个按键&#xff0c;但是至多有两个关键按键&am…

“QT快速上手指南”之计算器(一)Qt Creator,窗口组件

文章目录前言一、什么是QT&#xff1f;二、准备工作&#xff1a;1. 安装Qt Creator&#xff1a;2. 安装Qt SDK&#xff1a;3. 下载安装器&#xff1a;三、窗口组件&#xff1a;四、QT 基本组件的简单介绍&#xff1a;1. QWidget2. QPushButton3. QLabel4. QLineEdit5. QSpinBox…

智能火焰与烟雾检测系统(Python+YOLOv5深度学习模型+清新界面)

摘要&#xff1a;智能火焰与烟雾检测系统用于智能日常火灾检测报警&#xff0c;利用摄像头画面实时识别火焰与烟雾&#xff0c;另外支持图片、视频火焰检测并进行结果可视化。本文详细介绍基于智能火焰与烟雾检测系统&#xff0c;在介绍算法原理的同时&#xff0c;给出Python的…

FPGA基于XDMA实现PCIE X4视频采集HDMI输出 提供工程源码和QT上位机程序和技术支持

目录1、前言2、我已有的PCIE方案3、PCIE理论4、总体设计思路和方案5、vivado工程详解6、驱动安装7、QT上位机软件8、上板调试验证9、福利&#xff1a;工程代码的获取1、前言 PCIE&#xff08;PCI Express&#xff09;采用了目前业内流行的点对点串行连接&#xff0c;比起 PCI …

PE文件解析

PE结构图 工具101editor 文件实列 0-30h为DOS header 40-F0h为DOS STUB 100-1F0h为PE_HEADER 200-2B0h为SECTION_HEADER **虚拟地址&#xff1a;**文件加载到内存中&#xff0c;每个进程都有自己的4GB&#xff0c;这个4GB当中的某个位置叫做虚拟地址 基地址&#xff1a;文件…

23种设计模式之工厂方法模式(黑马程序员)

工厂方法模式一、概述二、结构三、实现四、总结在最后一、概述 定义一个用户创建对象的工厂(接口)&#xff0c;让接口的子类决定去实例化哪个对象&#xff0c;依赖其抽象编程。即工厂方法使一个类的实例化延迟到其工厂的子类。 二、结构 工厂方法模式的主要角色 抽象工厂&a…

计算机组成原理实验二 存储系统预习报告

实验一----静态RAM 一、实验目的 掌握静态随机存储器 RAM 工作特性及数据的读写方法。基于信号时序图&#xff0c;了解读写静态随机存储器的原理。 二、实验预习 1、阅读实验指导书&#xff0c;然后回答问题。 实验所用的静态存储器由一片 6116&#xff08;2K*8bit &#x…

ROS开发之如何在同一个节点订阅、处理、发布消息?

文章目录0、引言1、创建中间特殊文件&#xff08;含订阅者和发布者&#xff09;2、在CMakeLists.txt添加编译规则3、在launch添加启动项4、编译运行5、三维显示0、引言 在ROS应用一般会用到发布者和订阅者&#xff0c;若只接收传感器数据&#xff0c;则只实现订阅者就行&#x…

客户服务 KPI是什么

当企业着手改进其客户服务计划时&#xff0c;必须以可衡量的方式进行。因为如果我们为了改进而改进&#xff0c;没有衡量&#xff0c;我们就永远无法真正知道我们做得有多好&#xff01;如果您的公司已准备好升级其客户服务计划&#xff0c;这里有 12种方法可以衡量和跟踪您的客…

大学生问AI

大学生问AI写在最前面2&#xff0c;描述你在学习工作中碰到的最高级的 AI 是什么&#xff1f;1&#xff0c;你人生中第一次接触到 “人工智能” 的概念和产品是什么&#xff1f; 让你觉得 “人类做得东西的确有智能”&#xff1f;3&#xff0c;你听说过最近的 GPT&#xff0c;n…

Qt5.12實戰之Linux靜態庫與動態庫多文件生成a與so文件並調用

1.編輯並輸入內容到test.cpp與test2.cpp test.cpp #include <stdio.h> int func() {return 888; } test2.cpp #include <stdio.h> int func2() {return 999; } 將test.cpp與test2.cpp編譯成目標文件&#xff1a; g -c test.cpp test2.cpp 一次性生成目標文件…

阅读提升内在美

最近&#xff0c;想在内在美上提升自己&#xff0c;想把玩游戏的时间腾给阅读。不想只是善良&#xff0c;更希望自己拥有智慧拥有力量&#xff0c;更自信更热爱生活。 本篇博文会日日更新&#xff0c;也当作鼓励我继续阅读坚持阅读的动力和监督。 Z-Library 图书馆 分享一个…

LeetCode 1041. 困于环中的机器人

原题链接&#xff1a;1041. 困于环中的机器人 在无限的平面上&#xff0c;机器人最初位于 (0, 0) 处&#xff0c;面朝北方。注意: 北方向 是y轴的正方向。南方向 是y轴的负方向。东方向 是x轴的正方向。西方向 是x轴的负方向。 机器人可以接受下列三条指令之一&#xff1a; …

测试专家须精通Locust

目前随着AI人工智能越来越火&#xff0c;Python 编写的程序越来越多&#xff0c;更多的协议&#xff0c;更多的复杂应用。 所以Locust 是高级性能测试工程师和测试专家&#xff0c;必备技术之一&#xff0c;因为你不可能避免公司中使用python.编写接口或者程序。 在互联网公司…

C#|调用C/C++动态库

参考&#xff1a;C#总结&#xff08;四&#xff09;调用C动态库&#xff08;https://www.shuzhiduo.com/A/A2dmV49qze/&#xff09; 文章目录C#加载C动态库C#加载C#动态库涉及到的概念知识&#xff1a;托管DLL和非托管DLL的区别&#xff08;https://www.tinymind.net.cn/articl…

利用 ELK 处理 Percona 审计日志

Percona Server为 MySQL 数据库服务器进行了改进&#xff0c;在功能和性能上较 MySQL 有着很显著的提升。该版本提升了在高负载情况下的 InnoDB 的性能、为 DBA 提供一些非常有用的性能诊断工具&#xff1b;另外有更多的参数和命令来控制服务器行为 前提 1、有强烈的审计需求。…