day16_Set_Map

今日内容

零、 复习昨日
一、Set
二、Map

零、 复习昨日

  • 集合特点
    • 长度不固定
    • 存储的数据类型不限制
    • 有丰富api方法可以调用
    • 有些有序,无序,或者有些允许重复有些会去重
  • 集合体系图
  • List 集合, 规定了所存储的元素 有序且允许重复
  • 常用的ArrayList
    • 底层是数组,初始容量10
    • 存满后扩容,1.5倍
    • 查询或更新快
  • LinkedList
    • 底层是双向链表
    • 查找,更新较慢, 插入,删除较快

image-20240308095727002

一、Set

  • Set是Collection的子接口
  • 规定所存储的元素是不允许重复,且无序
    • HashSet的无序是将存储的元素随机排序
    • TreeSet的"无序" 是指将存储的元素给排序了
  • Set接口一般常用两个实现类
    • HashSet,存储的元素是无序且去重
    • TreeSet,存储的元素是去重且排序

二、HashSet

是Set接口的实现类,存储的元素是无序且去重,不保证线程安全

2.1 方法演示

HashSet() 空参构造,创建空集合

HashSet(Collection c) 创建带有元素的集合


方法都是常规方法,没有关于下标的操作

    public static void main(String[] args) {
        // 创建空集合
        HashSet<Integer> set = new HashSet<>( );
        System.out.println("初始空集合:" + set );

        // 添加元素(去重,且无序)
        boolean r1 = set.add(31);
        System.out.println("第1次添加元素:" + r1 );
        boolean r2 = set.add(31);
        System.out.println("第12次添加相同元素:" + r2 );

        set.add(21);
        set.add(11);
        set.add(51);
        set.add(41);
        System.out.println("加入元素后:" + set );

        int size = set.size( );
        System.out.println("集合大小:" + size );

        System.out.println("是否为空: "+ set.isEmpty() );

        System.out.println("是否包含:" + set.contains(11) );

        // set.clear();
        // System.out.println("清空集合后:"+ set );

        // 遍历
        for (Integer s : set){
            System.out.println(s );
        }
        // 通过指定集合创建Set
        HashSet<Integer> set2 = new HashSet<>(set);
        System.out.println("set2初始:" + set2 );
    }

2.2 底层实现原理

HashSet底层是HashMap,HashMap的底层实现是哈希表

// 创建HashSet时,会创建HashMap

image-20240311103422575

// 向hashSet中存储元素,其实向HashMap中存储

image-20240311103505852

HashSet(或者底层HashMap)创建时,默认容量是16,加载因子0.75

  • 容量就是存储的个数
  • 加载因子的作用是控制扩容的时机

什么时候触发开始扩容?

  • 当存储的元素达到 初始容量*加载因子时,即达到16 X 0.75 = 12,就会扩容,扩容成原来的2倍

通过调整初始容量和加载因子改变性能,但是建议是不要改

ps: hashMap的底层原理在jdk1.8后变样了,比较复杂,后续发资料

2.3 去重原理

需求: 创建学生类,定义2个属性,定义有参无参构造方法, 创建HashSet集合,泛型指定成Student,存储5个学生, 要求存储的学生如果属性一致要去重

// 学生类,正常的属性,封装,构造,toString

public class Student {

    private int age;
    private String name;
    // 略
}

// 测试

  public static void main(String[] args) {
        HashSet<Student> set = new HashSet<>( );
        set.add(new Student(18,"老王"));
        set.add(new Student(18,"老王"));
        set.add(new Student(19,"老李"));
        set.add(new Student(19,"老李"));
        set.add(new Student(20,"老刘"));
        set.add(new Student(20,"老刘"));

        for (Student student : set) {
            System.out.println(student );
        }
    }

// 结果是没有去重


为什么没有去重,通过源码发现,

  • 存储的学生对象,其实是存储的存到hashmap的键上
  • 然后通过存储的对象调用了hashcode方法,获得了地址值,但是学生类没有重写hashCode方法,所以调用的Object类的hashCode()方法,Object类的hashCode()方法是返回的对象地址,又因为每new一次地址值都不一样,所以即返回的每个对象地址值都不一样
  • 然后继续,发现源码中有 当正在存储的元素与集合中已经存在元素地址值如果相等时 会再判断元素的equals方法
  • 但是我们存储的元素的地址值都不一样,所以不会调用equals方法就直接存储到集合中了

去重原理:

  • 先调用存储的元素的hashcode方法,获得哈希值
  • 然后与集合中的元素的**地址值判断,如果不一样直接存储**
  • 如果存储的元素的地址值与集合中存在的元素的**地址值一样,再比较equals方法**,判断对象属性是否相同
  • 如果**equals也判断相同,即认为重复,不存储**
  • 如果**equals判断不相同,即认为不重复,存储**

2.4 总结

记住HashSet会去重,想要去重记得重写hashCode和equals方法即可

至于原理,怎么做到去重的会说就行!(面试)

三、TreeSet

TreeSet是Set集合的实现类,也是不允许重复元素,但是它存储的元素有序!!会对存储的元素默认按照自然顺序排序,另外此类也是不保证线程安全


TreeSet底层是TreeMap

3.1 演示方法

大部分方法都是常规方法,正常使用(添加,遍历,删除,大小,清空…)

 public static void main(String[] args) {
        // 使用空参构造创建TreeSet集合
        // 没有指定排序规则,默认是按照元素的自然顺序排序
        TreeSet<Integer> ts = new TreeSet<>( );
        /**
         * 去重,且排序
         */
        ts.add(3);
        ts.add(3);
        ts.add(2);
        ts.add(5);
        ts.add(1);
        ts.add(4);

        for (Integer i : ts) {
            System.out.println(i );
        }
    }

3.2 排序去重的原理

需求: 创建学生类,定义2个属性,定义有参无参构造方法, 创建TreeSet集合,泛型指定成Student,存储5个学生, 要求存储的学生如果属性一致要去重,按照年龄从小到大

发现无法去重,报错!!!


  1. 要想利用TreeSet排序,存储的元素必须实现Comparable接口,重写comparTo方法
  2. 当调用add方法存储元素时,会调用元素的comparTo进行运算
    1. 如果返回负数,放在二叉树左侧
    2. 如果返回正数,放在二叉树右侧
    3. 如果返回0,去除元素不放

// 学生类

public class Student implements Comparable<Student>{

    private int age;
    private String name;
    
    // set get 构造略写

    /**
     * compareTo方法的返回值决定了元素的顺序以及是否去重
     *
     * 返回值为 0 直接去重
     * 返回值为 负数 放根节点左侧
     * 返回值为 正数 放根节点右侧
     * -------------------------
     * 存储完,树按照中序遍历取值
     *------------------
     * o是树结构上以前存储过的元素
     * this是当前正在存的元素
     */
    @Override
    public int compareTo(Student o) {
        System.out.println("this:" + this );
        System.out.println("o:" + o );
        return  o.getAge() - this.getAge();
    }
}

// 测试

 public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<>( );
        ts.add(new Student(18,"老王1"));
        ts.add(new Student(18,"老王2"));
        ts.add(new Student(19,"老李3"));
        ts.add(new Student(19,"老李4"));
        ts.add(new Student(20,"老刘5"));
        ts.add(new Student(20,"老刘6"));
        for (Student s : ts) {
            System.out.println(s );
        }
    }

总结: 以后想要利用TreeSet进行排序去重,那么就得实现Comparable接口,重写comparTo方法,根据情况去写算法(确定如何正负零,可以试出来)

3.3 自然排序&比较器排序[扩展|了解]

自然排序其实就是指 实现了Comparable接口的,创建TreeSet集合时不指定排序规则,默认就是就这种自然排序

image-20240311160200544

比较器排序, 在创建集合时传入自定义的比较器对象,存储元素时就会使用自定义的方法实现排序

image-20240311160307814

演示: 自定义整型比较器,实现对存储整型Integer去重且倒序

// 自定义整型比较器

public class MyIntegerComparator implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
}

// 创建集合时存入该比较器对象

public static void main(String[] args) {  
    // 创建集合时存入该比较器对象
	TreeSet<Integer> set = new TreeSet<>(new MyIntegerComparator());
        set.add(3);
        set.add(3);
        set.add(2);
        set.add(5);
        set.add(1);
        set.add(4);

        System.out.println(set );
    }

总结:

  • 自然排序简单,且jdk中默认是使用这种,一般都是从小到大
  • 比较器排序,更灵活,可以自定义规则
  • 自然排序是需要类实现Comparable接口的,但是如果说源码无法改动,即无法实现Comparable接口完成排序,此时就可以使用比较器排序

四、Map

4.1 Map介绍

Map是一种映射关系的集合,能从键映射到值,它是一种特殊的集合,一次存储一对元素(键值对),即Map是双列集合

ps: 映射理解为就是查找

  • Map中不能包含重复的key(键)
  • Map中一个key只能找到一个值 (键找值)
  • Map中无法通过值找键的

Map是双列集合的接口,一般常用的是两个实现类: HashMap,TreeMap

4.2 HashMap[重点]

HashMap是Map的实现,

  • 底层是基于Hash表(jdk1.8以后底层是数组+链表+红黑树)
  • 允许存储null值null键
  • HashMap不保证线程安全
  • Hashtable是线程安全的,不允许null值null键,除此之外与HashMap

// 演示方法使用

    public static void main(String[] args) {
        // 创建空的map集合
        HashMap<Integer, String> map = new HashMap<>( );
        // 添加元素(键不允许重复,值可以重复)
        // 元素是无序
        String v1 = map.put(2, "二");
        System.out.println(v1 );// null,返回的是此key所对应之前的value

        String v2 = map.put(2, "贰");
        System.out.println(v2 );// 二

        map.put(22, "贰");
        map.put(32, "贰");
        map.put(42, "贰");
        map.put(52, "贰");
        // 输出结果
        System.out.println(map );

        // v get(Object key) // 通过键找值
        String v = map.get(22);
        System.out.println(v );

        // 判断是否包含某个键
        System.out.println(map.containsKey(22));
        // 判断是否包含某个值
        System.out.println(map.containsValue("22"));

        // 集合大小(尺寸),元素个数
        System.out.println(map.size() );

        // 根据键删除整个键值对,返回v
        String remove = map.remove(22);
        System.out.println("删除键22,返回对应的值:"+remove );
        System.out.println("删除后:" + map );

        // 清空集合
        map.clear();
        System.out.println("清空后:" + map );
    }

4.3 迭代遍历[重点]

Map集合没有提供直接的遍历Map集合的方法,但是提供了

  • 键集 Set keySet(),专门遍历键
  • 值集 Collection values.专门遍历值
  • 键值映射集 Set<Map.Entry<K,V>> entrySet,专门遍历键值对

演示遍历值集

		HashMap<String,Integer> map = new HashMap<>( );
        map.put("A",1);
        map.put("B",2);
        map.put("C",3);
        map.put("D",4);

        // 获得键的集合
        Set<String> keySet = map.keySet();
        // 遍历集合,得到所有的键
        for(String key:keySet){
            System.out.println(key );
        }

演示遍历值集

		// 获得值的集合
        Collection<Integer> values = map.values( );
        // 遍历
        for (Integer v : values){
            System.out.println(v );
        }

演示遍历键值映射集合

		// 获得键值映射集合
        // Map.Entry是Map内的内部接口,代表一项键值对
        Set<Map.Entry<String,Integer>> entrySet = map.entrySet();
        // 遍历
        for(Map.Entry<String,Integer> entry : entrySet) {
            String key = entry.getKey( );
            Integer value = entry.getValue( );
            System.out.println(key+"="+value );
        }

五、总结重点

今天重点

  • 记住HashSet的去重无序效果,熟练使用常用方法api即可
  • 记住HashMap的特点,熟练使用方法,以及三种遍历方式
  • 读背 hashset去重原理,treeset排序去重原理
    // Map.Entry是Map内的内部接口,代表一项键值对
    Set<Map.Entry<String,Integer>> entrySet = map.entrySet();
    // 遍历
    for(Map.Entry<String,Integer> entry : entrySet) {
    String key = entry.getKey( );
    Integer value = entry.getValue( );
    System.out.println(key+“=”+value );
    }
    ``

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LLM 技术图谱(LLM Tech Map) Kubernetes (K8s) 与AIGC的结合应用

文章目录 1、简介2、基础设施3、大模型3、AI Agent&#xff08;LLM Agent&#xff09;4、AI 编程5、工具和平台6、算力7、Kubernetes (K8s) 与人工智能生成内容 (AIGC) 的结合应用7.1、摘要7.2、介绍7.3、K8s 与 AIGC 的结合应用7.4、实践案例7.5、结论 1、简介 LLM 技术图谱&…

Elastic Stack--05--聚合、映射mapping

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.聚合(aggregations)基本概念桶&#xff08;bucket&#xff09;度量&#xff08;metrics&#xff09; 案例 11. 接下来按price字段进行分组&#xff1a;2. 若想对所…

C语言:预处理详解(知识点和代码演示)

和黛玉学编程........> 预定义符号 __FILE__ //进行编译的源文件 __LINE__ //文件当前的行号 __DATE__ //文件被编译的日期 __TIME__ //文件被编译的时间 __STDC__ //如果编译器遵循ANSI C&#xff0c;其值为1&#xff0c;否则未定义 比如&#xff1a; #define定义常量 #def…

Visual Studio2022使用(后续使用到再更新)

安装 从官网安装&#xff0c;下载即可。注意&#xff1a;选择社区版。&#xff08;免费够用&#xff09; 安装好后打开安装包&#xff0c;会继续下载东西&#xff0c;挂几个小时差不多就下好了。 使用 1、打开VS&#xff0c;点击“创建新项目”&#xff0c;选择“空项目”。…

【图论】 【割点】 【双连通分类】LCP 54. 夺回据点

本文涉及知识点 图论 割点 双连通分类 割点原理及封装好的割点类 LeetCode LCP 54. 夺回据点 魔物了占领若干据点&#xff0c;这些据点被若干条道路相连接&#xff0c;roads[i] [x, y] 表示编号 x、y 的两个据点通过一条道路连接。 现在勇者要将按照以下原则将这些据点逐一…

5分钟搭好一个易支付,个人最简单的对接支付宝方式

最近在疯狂折腾网站相关的知识,搭建了另一个平台后,需要涉及支付相关的内容。即 用户在某个平台请求支付时候,对接第三方支付支付宝,收款信息是我的,然后支付成功后给与回调。网上很多易支付网站,但是这玩意儿,既然咱碰到了,咱就自己弄。那么说搞咱就搞。 假设你已经搭…

Kafka MQ 生产者和消费者

Kafka MQ 生产者和消费者 Kafka 的客户端就是 Kafka 系统的用户&#xff0c;它们被分为两种基本类型:生产者和消费者。除 此之外&#xff0c;还有其他高级客户端 API——用于数据集成的 Kafka Connect API 和用于流式处理 的 Kafka Streams。这些高级客户端 API 使用生产者和消…

BUUCTF---easyre1

1.记录一下第一次做逆向题目 2.题目描述&#xff0c;下载附件 3.解压之后是一个可执行文件&#xff0c;先用PE查看是否有壳 4.没有壳&#xff0c;接下来用ida打开&#xff0c;直接拖进ida即可&#xff0c;接下来使用快捷键fnshiftf12查看字符&#xff0c;若是没有出现搜索框&a…

收割机案例-简单的动态规划

#include<iostream> using namespace std; // 创建土地 short land[32][32]; short n,m;// 实际使用的土地大小 short landA[32][32];//用A收割机收割数量记录 short landB[32][32];// 用B收割机收割数量记录 int main(){cin>>n>>m;// 存储农作物产量for(sho…

GNN-Transformer新突破!全局与局部的完美融合

图神经网络&#xff08;GNN&#xff09;和Transformer的结合是近年来的研究热点。这类结合不仅能够让两者发挥各自的优势&#xff0c;还能推动模型的创新&#xff0c;提高处理图数据的效率和性能。 具体点讲&#xff0c;通过利用Transformer&#xff0c;我们可以扩展GNN的感受…

Spring boot 操作 Redis

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

nginx-排查一次大文件无法正常下载问题

目录 问题现象&报错信息 问题现象以及分析 nginx报错信息 问题解决 方法1&#xff1a;配置proxy_max_temp_file_size 方法2&#xff1a;关闭proxy_buffering 参考文档 问题现象&报错信息 问题现象以及分析 文件正常从后端服务器直接下载时&#xff0c;一切正常…

【git bug】warning: auto-detection of host provider took too long (>2000ms)

【git bug】warning: auto-detection of host provider took too long (>2000ms) 报错问题&#xff1a; warning: auto-detection of host provider took too long (>2000ms) 报错截图&#xff1a; 报错描述&#xff1a; 在windows操作系统&#xff0c;未连接互连网电…

java之mybatis

准备工作 上面4步骤 XML映射文件 动态SQL

掘根宝典之c++有符号(signed)和无符号类型(unsigned)

在c中&#xff0c;有符号和无符号是针对整型而言的 在C中&#xff0c;除了布尔类型和拓展的字符类型之外&#xff0c;其他整型可以划分为有符号类型&#xff08;signed&#xff09;和无符号类型&#xff08;unsigned&#xff09;&#xff0c;用于表示整数。 有符号类型可以表…

参与Penpad launch任务,实现Penpad与Scroll的双空投

在比特币 ETF 、BTC 减半等利好消息的持续推动下&#xff0c;加密市场逐渐进入到新一轮牛市周期中。除了以太坊 Layer1 生态 TVL 不断飙升外&#xff0c;Layer2 赛道 TVL 也在不断飙升并且屡创新高。 而在牛市背景下&#xff0c;Layer2 空投所带来的财富效应预期正在被进一步拉…

AD20新建工程步骤

1 新建工程 2 创建 3 新建原理图 4 新建PCB图 5 对原理图贺PCB都进行保存 6 新建原理图库贺PCB库&#xff0c;以及保存 最后在保存位置上都可以看到 打开的时候直接打开工程&#xff0c;它自己就会把这些链接在一起

笔记本电脑使用时需要一直插电吗?笔记本正确的充电方式

随着科技的不断发展&#xff0c;笔记本电脑已经成为人们日常生活和工作中不可或缺的电子设备。而在使用笔记本电脑时&#xff0c;很多人会有一个疑问&#xff0c;那就是笔记本电脑使用时需要一直插电吗&#xff1f;本文将就此问题展开讨论。 不一定需要一直插电&#xff0c;如果…

Linux网络基础3之IP协议

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff01;你好这里是ky233的主页&#xff1a;这里是ky233的主页&#xff0c;欢迎光临~https://blog.csdn.net/ky233?typeblog 点个关注不迷路⌯▾⌯ 目前已经学完了应用层以及传输层&#xff0c;我们应…

系统学习c++类和对象——深度理解默认成员函数

前言&#xff1a;类和对象是面向对象语言的重要概念。 c身为一门既面向过程&#xff0c;又面向对象的语言。 想要学习c&#xff0c; 首先同样要先了解类和对象。 本节就类和对象的几种构造函数相关内容进行深入的讲解。 目录 类和对象的基本概念 封装 类域和类体 访问限定符…