【Java集合】Set接口及系列子类HashSet等

news2024/11/25 14:52:01

文章目录

  • Set接口
    • > Set 接口和常用方法
    • > Set接口实现类 - HashSet
      • HashSet 底层机制(HashMap)
    • > Set接口实现类 - LinkedHashSet

Set接口

在这里插入图片描述

Set 接口介绍:

  1. 无序(添加和取出的顺序不一致),没有索引;
  2. 不允许重复元素,所以最多包含一个null;
  3. JDK API 中Set的常用实现类有:HashSet 和 TreeSet;

> Set 接口和常用方法

Set 接口的常用方法

  • 和 List 接口一样,Set 接口也是 Collection 的子接口,所以常用方法和Collection接口一样

Set 接口的遍历方式

  • 同 Collection 的遍历一样:
    • 迭代器遍历
    • 增强 for
    • 不能用索引 的方式来获取; (因为Set无序
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetMethod {
    public static void main(String[] args) {
        //以Set接口的实现类 HashSet 来演示
        //Set接口的实现类对象(Set接口对象),不能存放重复元素
        //Set接口对象存放和读取数据无序
        //取出的顺序虽然不是添加的顺序,但是,是固定有序的
        Set set = new HashSet();
        set.add("John");
        set.add("Lucy");
        set.add("Jack");
        set.add(null);
        set.add(null);
        System.out.println(set);//[null, John, Lucy, Jack] 执行多遍都是这个结果
		set.remove(null);//等常用方法可以依照Colleciotn常用方法,是一致的

        //遍历:迭代器
        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            Object o = iterator.next();
            System.out.println(o);
        }
        //遍历:增强for (底层还是迭代器)
        for(Object o:set){
            System.out.println(o);
        }
        //不能索引遍历,且set接口对象没有get()方法
    }
}

> Set接口实现类 - HashSet

  1. HashSet实现了Set接口;
  2. HashSet实际上是HashMap,可以从源码看出;
  3. 可以存放 null 值,但是只能有一个null;
  4. HashSet 不保证元素是有序的,取决于hash后,再确定索引的结果;
  5. 不能有重复元素 / 对象;
import java.util.HashSet;

public class HashSet01 {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        //1.在执行add方法后,会返回一个boolean值
        //2.如果添加成功,返回true,否则返回false
        System.out.println(hashSet.add("john"));//true
        System.out.println(hashSet.add("lucy"));//true
        System.out.println(hashSet.add("john"));//false
        System.out.println(hashSet.add("jack"));//true
        System.out.println(hashSet.add("rose"));//true
        hashSet.remove("john");//指定删除某对象
        System.out.println("hashset = "+hashSet);//hashset = [rose, lucy, jack]

        hashSet = new HashSet();
        //HashSet不能添加相同的元素、数据
        hashSet.add("lucy");//添加成功
        hashSet.add("lucy");//加入不了
        hashSet.add(new Dog("tom"));//OK
        hashSet.add(new Dog("tom"));//也能加入
        System.out.println("hashset = "+hashSet);//hashset = [Dog{name='tom'}, lucy, Dog{name='tom'}]
        //经典面试题
        hashSet.add(new String("ok"));//可以加入
        hashSet.add(new String("ok"));//无法加入
        System.out.println("hashset = "+hashSet);//hashset = [Dog{name='tom'}, ok, lucy, Dog{name='tom'}]
        //看源码 add到底发生了什么 --》底层机制
    }
}
class Dog{
    private String name;

    public Dog(String name) {
        this.name = name;
    }

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

HashSet 底层机制(HashMap)

HashSet 底层其实是HashMap,HashMap底层是(数组+链表+红黑树)

模拟数组+链表结构:

  1. 定义一个数组
  2. 数组里面放对象
  3. 一个对象还能指向下一个对象
public class HashSetStructure {
    public static void main(String[] args) {
        //模拟一个HashSet的底层(HashMap)
        //1.创建一个数组,数组的类型是Node[]
        //2.Node[] 也称为一个表
        Node[] table = new Node[16];
        //3.创建一个节点
        Node john = new Node("john", null);
        table[2] = john;
        Node jack = new Node("jack", null);
        john.next = jack;//将节点挂载到john
        Node rose = new Node("rose",null);
        jack.next = rose;//将rose节点挂载到jack
        Node lucy = new Node( "lucy",null);
        table[3] = lucy;//把lucy放到table表的索引为3的位置
        System.out.println("table = "+table);
    }
}
class Node{//节点,存储数据,可以指向下一个节点,从而形成链表
    Object item;//存放数据
    Node next;//指向下一个节点

    public Node(Object item, Node next) {
        this.item = item;
        this.next = next;
    }
}

debug后解读:
在这里插入图片描述

HashSet底层机制:
在这里插入图片描述

  1. HashSet 底层其实是 HashMap;
  2. 添加一个元素时,先得到 hash值-> 转成->索引值 ;
  3. 找到存储数据表 table ,看这个索引位置是否已经存放的所有元素;
  4. 如果没有,直接加入;
  5. 如果有,调用 equals 比较,如果相同,就放弃添加,如果不相同,则添加到最后;
  6. 在Java8中,如果一条链表的元素个数达到 TREEIFY_THRESHOLD(默认是8),并且table大小>=MIN_TREEIFY_CAPACITY(默认是64),就会进行树化(红黑树);
用例:
	 定义一个Employee类,该类包含:private成员属性name,age 
要求:
	 1.创建3个Employee对象放入HashSet中;
	 2.当name和age的值相同时,认为是相同员工,不能添加到HashSet集合中;
import java.util.HashSet;
import java.util.Objects;

public class HashSet_Exercise {
    /**
     * 定义一个Employee类,该类包含:private成员属性name,age
     * 1.创建3个Employee对象放入HashSet中;
     * 2.当name和age的值相同时,认为是相同员工,不能添加到HashSet集合中;
     */
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("jack",18));
        hashSet.add(new Employee("tom",28));
        hashSet.add(new Employee("rose",18));
        //加入了三个成员
        System.out.println(hashSet);//[Employee{name='jack', age=18}, Employee{name='rose', age=18}, Employee{name='tom', age=28}]
    }
}

//创建Employee
class Employee{
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    //如果name和age相同,则返回相同的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);
    }
    //name和age相同,hashcode相同
    @Override 
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

> Set接口实现类 - LinkedHashSet

在这里插入图片描述

  1. LinkedHashSet 是 HashSet 的子类,继承HashSet,实现了Set接口;
  2. LinkedHashSet 底层是一个 LinkedHashMap,底层维护了一个 数组+双向链表;
  3. LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的;
  4. LinkedHashSet 不允许添加重复元素;
    在这里插入图片描述
import java.util.LinkedHashSet;
import java.util.Set;

public class LinkedHashSetSource {
    public static void main(String[] args) {
        //LinkedHashSet底层机制
        Set set = new LinkedHashSet();
        set.add(new String("OK"));
        set.add(128);
        set.add(128);
        set.add(new Customer("靳",1201));
        set.add("JinYu");
        System.out.println(set);
        //[OK, 128, com.study.set_.Customer@677327b6, JinYu]
        /*
        1.添加元素和取出顺序一致
        2.LinkedHashSet底层维护的是一个LinkedHashMap(是HashMap的子类)
        3.LinkedHashSet底层结构:数组table+双向链表
        4.添加第一次时,直接将数组table扩容到16,存放的结点类型是LinkedHashSetMap$Entry
        5.数组是HashMap$Node[] 存放的元素/数据是LinkedHashSetMap$Entry类型
        */
    }
}
class Customer{
    private String name;
    private int id;

    public Customer(String name, int id) {
        this.name = name;
        this.id = id;
    }
}
示例:Car类(属性name,price),如果name和price一样,则认为是相同元素,就不能添加
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;

public class LinkedHashSetExercise {
    public static void main(String[] args) {
        Set set = new LinkedHashSet();
        set.add(new Car("奥拓",1000));
        set.add(new Car("奥迪",300000));
        set.add(new Car("法拉利",9000000));
        set.add(new Car("奥迪",300000));
        set.add(new Car("保时捷",1000));
        set.add(new Car("奥迪",300000));
        System.out.println(set);
        /* 未重写equals和hashCode方法:
        [Car{name='奥拓', price=1000.0}
        , Car{name='奥迪', price=300000.0}
        , Car{name='法拉利', price=9000000.0}
        , Car{name='奥迪', price=300000.0}
        , Car{name='保时捷', price=1000.0}
        , Car{name='奥迪', price=300000.0}
        ]*/
        /* 重写equals和hashCode方法后:
        [Car{name='奥拓', price=1000.0}
        , Car{name='奥迪', price=300000.0}
        , Car{name='法拉利', price=9000000.0}
        , Car{name='保时捷', price=1000.0}
        ]
         */
    }
}
class Car{
    private String name;
    private double price;

    public Car(String name, double price) {
        this.name = name;
        this.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;
    }

    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}'+"\n";
    }
    //重写equals方法和hashCode方法
    //当name和price相同时,返回相同的hashCode值
    @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);
    }
}

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

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

相关文章

交叉开发环境搭建

ubuntu网络环境搭建 配置网络环境有很多种方法,可以用命令行也可以用图形化界面。ip可以是静态的也可以是动态的。当然要是用SSH访问的话要配置成静态的,但是用校园网的话,又要是动态的,这里就不详细说了。 我们配置ubuntu是为了能…

Java学习路线图(2023版,视频已更新)

PS:注意收藏,此套路线图会不定期更新!点这里跳转:2023年Java程序员学习路线图入门: Java SE基础 → Java Web(含数据库H5jsvue)中级: Maven → Git → SSM框架 → MybatisPlus → Spring Boot→ 《传智健康》项目实战 …

北大硕士LeetCode算法专题课-基础算法查找

算法专题系列: 北大硕士LeetCode算法专题课---算法复杂度介绍_骨灰级收藏家的博客-CSDN博客 北大硕士LeetCode算法专题课-基础算法之排序_骨灰级收藏家的博客-CSDN博客 查找算法 查找算法也可以叫搜索算法。 查找算法就是从一个有序数列中找出一个特定的数&am…

66.物体检测算法:区域卷积神经网络(R-CNN)系列

1. R-CNN ps:在计算机视觉中,深度学习之前,分类器用的是SVM 2. 兴趣区域(RoI)池化层 目的是为了让每个锚框都可以变成一个自己想要的形状。 3. Fast RCNN 具体步骤如下: 对整张图片用CNN抽特征&#xff…

IT服务管理(ITSM)是什么?ITSM工具哪个好用

什么是IT服务管理(ITSM) IT 服务管理 (ITSM) 包含一组策略和实践,这些策略和实践可用于为最终用户实施、交付和管理 IT 服务,以满足最终用户的既定需求和企业的既定目标。 在此定义中,最终用户可以包含员工、客户或业…

一网打尽链表的经典OJ题!链表必考笔试题第一弹

目录 0.前言 1. 移除链表元素 2. 反转链表 2.1 方法一(遍历反转链接关系) 2.2 方法二(节点头插构造新链表) 3.链表的中间节点 4. 链表中倒数第k个节点 5. 总结 0.前言 本文所有代码都已传入gitee,可自取 3链表OJ题p1 onlookerzy123456qwq/data_structu…

使用Electron创建桌面程序,从创建到打包

在桌面程序中,使用C#语言可以创建winform和WPF程序,他们2个在Windows中都非常的优秀,还有就是使用QT开发桌面,可以跨平台开发,这三种都是比较“正规”的,而Electron是使用框架开发桌面程序的,还…

JDBC核心技术_第9章:Apache-DBUtils实现CRUD操作

目录9.1 Apache-DBUtils简介9.2 主要API的使用9.2.1 DbUtils9.2.2 QueryRunner类9.2.3 ResultSetHandler接口及实现类9.1 Apache-DBUtils简介 commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低&#xff0…

硬件设备上也能安全运行小程序

当前,在百度、支付宝、今日头条等各大巨头都把持小程序技术尚未开放的情况下,市面上可商用的小程序技术选择面非常狭窄。与此同时,企业仍希望实现 “一次开发,多端运行”,从而真正达到降本增效。今天为大家分享一下&am…

【c++复习梳理】--基础入门语法

目录 1.函数重载 1.1 函数重载概念 1.2C支持函数重载的原理--名字修饰(name Mangling) 2.引用 2.1 引用概念 2.2 引用特性 2.3 常引用 2.4 使用场景 2.4.1 做参数 2.4.2 做返回值 2.5 传值、传引用效率比较 2.5.1 传值、传引用效率比较--做参数返回 2.5.2 值和引用…

Linux--进程地址空间在线程方面的补充--页表的操作模式 0109

上一篇有关地址空间的博客 (入门自用)--Linux--程序地址空间--程序的创建--0907-0913_Gosolo!的博客-CSDN博客 很久之前写的,最近会完善。 1. 进程地址空间在线程方面的补充 线程由于资源都是从主进程直接拿来的,所以他们的pcb结构体中的地址…

如何让 Shell 提示符更酷炫

使用远程终端时,默认的命令行提示符格式已经能满足大部分用户需求了,但有时我们希望提示符看起来更直观、优雅、酷炫、美观,可以从中直接得到我们想要的信息,而且清晰分明。本文就详细讲解一下如何让 Shell 提示符更酷炫&#xff…

13_9、Java的IO流之NIO.2中Path、Paths、Files类的使用

一、引入1、Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于通道的…

cinder对接nfs后端存储

1.部署配置流程 1.安装nfs服务端 可以新增一个节点,或直接使用计算节点,因为存储节点上已经有lvm了这里直接使用计算节点来安装 yum install nfs-utils -y2.修改配置 vi /etc/exports # 要共享的目录 允许的网络1(操做权限) 允许的网络2(操做权限) …

Mybatis源码分析(八)MapperMethod的Select分析

目录一 Select1.1 参数的对应解析convertArgsToSqlCommandParam1.2 ID获取对应的MappedStatement1.3 MappedStatement交给执行器执行1.4 根据参数获取BoundSql1.5 SqlNode节点的解析1.5.1 MixedSqlNode1.5.2 IfSqlNode1.5.3 StaticTextSqlNode1.5.4 TextSqlNode1.6 执行器执行查…

Delete `␍`eslint(prettier/prettier) 错误的解决方案

Delete ‘␍’ eslint(prettier/prettier) 错误的解决方案 问题背景 在Windows笔记本上新拉完代码,在执行pre-commit时,出现如下错误: Delete ␍eslint(prettier/prettier)下面是几种个人尝试过的解决方案: 解决方案 一、Crtl…

【自学Python】Python bytes转string

Python bytes转string Python string转bytes教程 在 Python 中,bytes 类型和 字符串 的所有操作、使用和内置方法也都基本一致。因此,我们也可以实现将 bytes 类型转换成 string 类型。 Python bytes转string方法 Python bytes 转 string 方法主要有…

effective c++读书笔记4

设计class犹如设计type新type的对象应该如何被创建和销毁?:这会影响到你的class的构造函数和析构函数以及内存分配函数和释放函数的设计。对象的初始化和对象的赋值有什么样的区别?这答案决定你的构造函数和赋值操作符的行为,以及…

分享一套Springboot个人博客系统源码带本地搭建教程

Springboot个人博客系统源码带本地搭建教程,需要源码学习可私信我获取。 技术架构 前端框架:JQuery SemanticUI Markdown prism animatecss Tocbot zplayer lightbox 后端框架:SpringBoot 2.2.5 Mybatis Thymeleaf PageHelper m…

基于JAVA SSM springboot实现的抗疫物质信息管理系统设计和实现

基于JAVA SSM springboot实现的抗疫物质信息管理系统设计和实现 博主介绍:5年java开发经验,专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 …