Java 集合 (Set, TreeSet, HashSet)

2023-11-07

1.Set

在这里插入图片描述

Set 集合特点:

  • 可以去除重复。
  • 存取顺序不一致。
  • 没有带索引的方法,所以不能使用普通 for 循环遍历,也不能通过索引来获取、删除 Set 集合里面的元素。
package com.qdu.myset;

import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

public class MySet1 {
    public static void main(String[] args) {
        Set<String> set = new TreeSet<>();
        set.add("ccc");
        set.add("aaa");
        set.add("aaa");
        set.add("bbb");

        // Set集合是没有索引的,所以不能使用通过索引获取元素的方法

        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }

        System.out.println("-----------------------------------");

        for (String s : set) {
            System.out.println(s);
        }
    }
}

2.TreeSet

TreeSet 集合特点:

  • 不包含重复元素
  • 没有带索引的方法
  • 可以将元素按照规则进行排序
package com.qdu.mytreeset;

import java.util.TreeSet;

public class MyTreeSet1 {
    public static void main(String[] args) {
        TreeSet<Integer> ts = new TreeSet<>();
        ts.add(5);
        ts.add(3);
        ts.add(4);
        ts.add(1);
        ts.add(2);

        System.out.println(ts); // [1, 2, 3, 4, 5]
    }
}

2.1 自然排序

想要使用 TreeSet,需要制定排序规则。

自然排序 Comparable 的使用:

  • 使用空参构造创建 TreeSet 集合。
  • 自定义的 Student 类实现 Comparable 接口。
  • 重写里面的 compareTo 方法。
    • 如果返回值为负数,表示当前存入的元素是较小值,存左边
    • 如果返回值为 0,表示当前存入的元素跟集合中元素重复了,不存
    • 如果返回值为正数,表示当前存入的元素是较大值,存右边
package com.qdu.mytreeset;

public class Student implements Comparable<Student> {
    private String name;
    private int age;

    public Student() {
    }

    public Student(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 "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        // 按照对象的年龄进行排序
        int result = this.age - o.age;
        return result;
    }
}
package com.qdu.mytreeset;

import java.util.TreeSet;

public class MyTreeSet2 {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<>();

        Student s1 = new Student("zhangsan", 29);
        Student s2 = new Student("lisi", 27);
        Student s3 = new Student("wangwu", 23);
        Student s4 = new Student("zhaoliu", 27);

        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);

        System.out.println(ts);
        // [Student{name='wangwu', age=23}, Student{name='lisi', age=27}, Student{name='zhangsan', age=29}]
    }
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

要求:按照年龄从小到大排,如果年龄一样,则按照姓名首字母排序。如果姓名和年龄一样,才认为是同一个学生对象,不存入。

package com.qdu.mytreeset;

public class Student implements Comparable<Student> {
    private String name;
    private int age;

    public Student() {
    }

    public Student(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 "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        // 主要判断条件
        int result = this.age - o.age;
        // 次要判断条件
        result = result == 0 ? this.name.compareTo(o.getName()) : result;
        return result;
    }
}
package com.qdu.mytreeset;

import java.util.TreeSet;

public class MyTreeSet2 {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<>();

        Student s1 = new Student("zhangsan",29);
        Student s2 = new Student("zhaoliu",27);
        Student s3 = new Student("wangwu",23);
        Student s4 = new Student("lisi",27);

        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);

        System.out.println(ts);
        // [Student{name='wangwu', age=23}, Student{name='lisi', age=27}, Student{name='zhaoliu', age=27}, Student{name='zhangsan', age=29}]
    }
}

2.2 比较器排序

比较器排序 Comparator 的使用:

  • TreeSet 的带参构造方法使用的是比较器排序对元素进行排序的。
  • 比较器排序,就是让集合构造方法接收 Comparator 的实现类对象,重写 compare(T o1, T o2) 方法。
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写。

示例:自定义 Teacher 老师类,属性为姓名和年龄。请按照年龄排序,如果年龄一样,则按照姓名进行排序。姓名都使用英文字母表示。

package com.qdu.mytreeset;

public class Teacher {
    private String name;
    private int age;

    public Teacher() {
    }

    public Teacher(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 "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.qdu.mytreeset;

import java.util.Comparator;
import java.util.TreeSet;

public class MyTreeSet4 {
    public static void main(String[] args) {
        TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
            @Override
            public int compare(Teacher o1, Teacher o2) {
                // o1表示现在要存入的那个元素
                // o2表示已经存入到集合中的元素
                
                // 主要条件
                int result = o1.getAge() - o2.getAge();
                // 次要条件
                result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
                return result;
            }
        });

        Teacher t1 = new Teacher("zhangsan",23);
        Teacher t2 = new Teacher("lisi",22);
        Teacher t3 = new Teacher("zhaoliu",24);
        Teacher t4 = new Teacher("wangwu",24);

        ts.add(t1);
        ts.add(t2);
        ts.add(t3);
        ts.add(t4);

        System.out.println(ts);
        // [Teacher{name='lisi', age=22}, Teacher{name='zhangsan', age=23}, Teacher{name='wangwu', age=24}, Teacher{name='zhaoliu', age=24}]
    }
}

2.3 两种比较方式小结

自然排序:自定义类实现 Comparable 接口,重写 compareTo 方法,根据返回值进行排序。

比较器排序:创建 TreeSet 对象的时候传递 Comparator 的实现类对象,重写 compare 方法,根据返回值进行排序。

在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,使用比较器排序。

两种方式中,关于返回值的规则:

  • 如果返回值为负数,表示当前存入的元素是较小值,存左边
  • 如果返回值为 0,表示当前存入的元素跟集合中元素重复了,不存
  • 如果返回值为正数,表示当前存入的元素是较大值,存右边

示例:存入四个字符串 “c”, “ab”, “df”, “qwer”,按照长度排序,如果一样长则按照首字母排序。

package com.qdu.mytreeset;

import java.util.Comparator;
import java.util.TreeSet;

public class MyTreeSet5 {
    public static void main(String[] args) {
        TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                int result = o1.length() - o2.length();
                result = result == 0 ? o1.compareTo(o2) : result;
                return result;
            }
        });

//        TreeSet<String> ts = new TreeSet<>(
//                (String o1, String o2) -> {
//                    int result = o1.length() - o2.length();
//                    result = result == 0 ? o1.compareTo(o2) : result;
//                    return result;
//                }
//        );

        ts.add("c");
        ts.add("ab");
        ts.add("df");
        ts.add("qwer");

        System.out.println(ts); // [c, ab, df, qwer]
    }
}

3.HashSet

HashSet 集合特点:

  • 底层数据结构是哈希表
  • 不能保证存储和取出的顺序完全一致
  • 没有带索引的方法,所以不能使用普通 for 循环遍历
  • 由于是 Set 集合,所以元素唯一
package com.qdu.myhashset;

import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo1 {
    public static void main(String[] args) {
        HashSet<String> hs = new HashSet<>();
        hs.add("hello");
        hs.add("world");
        hs.add("java");
        hs.add("java");
        hs.add("java");
        hs.add("java");
        hs.add("java");
        hs.add("java");

        Iterator<String> it = hs.iterator();
        while(it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }

        System.out.println("------------------------");

        for (String s : hs) {
            System.out.println(s);
        }
    }
}

3.1 哈希值

哈希值(哈希码值):是 JDK 根据对象的地址或者属性值算出来的 int 类型的整数。

Object 类中有一个方法可以获取对象的哈希值:

  • public int hashCode():根据对象的地址值计算出哈希值。

对象的哈希值特点:

(1) 如果没有重写 hashCode 方法,那么是根据对象的地址值计算出哈希值。

  • 同一个对象多次调用 hashCode() 方法返回的哈希值是相同的。
  • 不同对象的哈希值是不一样的。

(2) 如果重写了 hashCode 方法,一般都是通过对象的属性值计算出哈希值。

  • 如果不同的对象属性值是一样的,那么计算出来的哈希值也是一样的。
package com.qdu.myhashset;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(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 "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.qdu.myhashset;

public class HashSetDemo2 {
    public static void main(String[] args) {
        Student s1 = new Student("xiaozhi",23);
        Student s2 = new Student("xiaomei",22);

        // Object类中的hashCode方法,根据对象的地址值计算出哈希值
        System.out.println(s1.hashCode()); // 1324119927
        System.out.println(s1.hashCode()); // 1324119927

        System.out.println(s2.hashCode()); // 990368553
    }
}

示例:创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合。学生对象的成员变量值相同,就认为是同一个对象。

package com.qdu.hashsettest;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(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 boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

	// 重写了hashCode方法,根据对象的属性值来计算哈希值,此时跟对象的地址值就没有任何关系了
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.qdu.hashsettest;

import java.util.HashSet;

public class HashSetTest1 {
    public static void main(String[] args) {
        HashSet<Student> hs = new HashSet<>();
        // 如果HashSet集合要存储自定义对象,那么必须重写hashCode和equals方法

        Student s1 = new Student("xiaohei",23);
        Student s2 = new Student("xiaohei",23);
        Student s3 = new Student("xiaomei",22);

        hs.add(s1);
        hs.add(s2);
        hs.add(s3);

        for (Student student : hs) {
            System.out.println(student);
        }
        // Student{name='xiaohei', age=23}
        // Student{name='xiaomei', age=22}
    }
}

3.2 底层原理

JDK8 之前(不包括JDK8),底层采用数组+链表实现。

JDK8以后,底层进行了优化,由数组+链表+红黑树实现。

HashSet1.7版本原理

在这里插入图片描述

HashSet1.8版本原理

在这里插入图片描述

在这里插入图片描述

4.小结

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 集合 (Set, TreeSet, HashSet) 的相关文章

随机推荐

  • Window.iso镜像文件下载

    Window iso镜像文件下载 https www microsoft com zh cn software download windows10 Window iso官网下载地址 一 使用MediaCreationTool20H2官方工
  • MCU(单片机)datasheet(规格说明书)

    STC 宏晶 型号 程序容量 datasheet STC8G2K64S4 36I QFN48 64K 规格说明书链接 https pan baidu com s 18IickcTlMHgesCkmXFi7Lg pwd tala 提取码 ta
  • gcc、g++、make、cmake区别

    首先介绍一下GCC GNU Compiler Collection GNU 编译器集合 在为Linux开发应用程序时 绝大多数情况下使用的都是C语言 因此几乎每一位Linux程序员面临的首要问题都是如何灵活运用C编译器 目前 Linux下最
  • 【游戏测试工程师】2023年4399秋招笔试+面试记录

    文章目录 一 笔试题 一 逻辑推理题 1 猜花色 2 猜岗位 二 游戏测试相关题 1 假如你要对一款手机游戏进行基本测试 问 一 你会做哪几个方面的测试 二 分别想怎么做 2 编写游戏测试用例 3 编写测试点 三 编程题 1 游戏竞技场 2
  • java swing GUI窗口美化

    一般我们写出的窗口是这个样子的 文本框和按钮都不是太美观 如果按钮是原色的就更难看了 今天发现了一个更加美观的窗口模式 可以发现按钮和文本框都已经有了变化 给窗口润色不少 其实 只需在调用程序前加上这段代码即可 try for javax
  • Atom+Asciidoctor+Antora环境搭建

    个人博客原文链接 Atom 简介 Atom是github专门为程序员推出的一个跨平台文本编辑器 具有简洁和直观的图形用户界面 并有很多有趣的特点 支持CSS HTML JavaScript等网页编程语言 它支持宏 自动完成分屏功能 集成了文
  • LeetCode——040

    40 Combination Sum II My Submissions QuestionEditorial Solution Total Accepted 66386 Total Submissions 241547 Difficulty
  • 搭建Mybatis注意事项和实现原理,你真的看懂了吗?

    搭建Mybatis环境注意事项 一 搭建Mybatis环境注意事项 二 涉及到的设计模式 2 1工厂模式 SqlSessionFactory 2 2 代理模式 MapperProxyFactory 2 3构建者模式 SqlSessionFa
  • openpyxl绘制堆叠图

    本文将会说明如何用openpyxl绘制堆叠图 先来看看效果图 数据处理后效果展示 源数据 老规矩源代码先放上后面再解析 使用时记得要改一下路径哦 先把openpyxl全家桶安排上 from openpyxl import load work
  • Python基础08

    Python基础08 学习08 方法没有重载 在其他语言中 可以定义多个重名的方法 只要保证方法标签名唯一即可 方法签名包含3个部分 方法名 参数数量 参数类型 Python中 方法的参数没有生命类型 调用时确定参数的类型 参数的数量也可以
  • hive文件存储格式:SequenceFile系统总结

    问题导读 1 什么是SequenceFile 2 如何 通过 源码实现SequenceFile压缩 3 SequenceFile格式压缩有什么优点和缺点 1 SequenceFile是什么1 1 SequenceFile概述 1 1 seq
  • mapGetters 辅助函数

    1 mapGetters 辅助函数 mapGetters 辅助函数 mapGetters 辅助函数仅仅将store 中的 getter 映射到局部计算属性 1 import mapGetters from vuex 2 export def
  • 列出一个文件夹下的所有文件名(全路径)

    需求 列出一个文件夹下的所有文件的全路径文件名 如果文件是文件夹 那么再次遍历这个文件夹的的所有文件名 直到遍历的文件不是文件夹为止 这时输出文件的全路径名 分析 可以用函数实现 1 函数参数1个 就是要遍历的文件夹的名字 2 遍历该文件夹
  • java垃圾回收机制详解

    文章目录 一 垃圾回收机制是什么 二 机制运行原理 三 机制运行步骤 一 垃圾回收机制是什么 Java的垃圾回收 Garbage Collection GC 机制是Java内存管理的核心部分 它可以自动回收不再被程序使用的内存空间 防止内存
  • Makefile 中:= ?= += =的区别

    在Makefile中我们经常看到 这几个赋值运算符 那么他们有什么区别呢 我们来做个简单的实验 新建一个Makefile 内容为 ifdef DEFINE VRE VRE Hello World else endif ifeq OPT de
  • 今天Chat GPT又胡说八道,看我如何纠正的

    今天想把python pydantic 转成markdown mermaid 就去问Chat GPT Chat GPT给出了如下答案 然后 我就打入命令安装 结果这个包根本就不存在 既然找不到 pydantic 转 mermaid 的工具
  • Docker 安装hadoop

    一 实验环境 实验设备 硬件 单核CPU 内存1G 软件 Ubuntu 16 04操作系统 Docker Hadoop 二 实验原理及内容 1 在Ubuntu系统中安装Docker Apt install docker 复制代码 Apt g
  • Kendo UI开发教程(9): Kendo UI Validator 概述

    Kendo UI Validator 支持了客户端校验的便捷方法 它基于HTML 5 的表单校验功能 支持很多内置的校验规则 同时也提供了自定义规则的便捷方法 完整的Kendo UI 的Validator可以参见API 文档 HTML 5
  • SQL优化

    1 应尽量避免在 where 子句中使用 或 lt gt 操作符 否则将引擎放弃使用索引而进行全表扫描 2 对查询进行优化 应尽量避免全表扫描 首先应考虑在 where 及 order by 涉及的列上建立索引 3 应尽量避免在 where
  • Java 集合 (Set, TreeSet, HashSet)

    目录 1 Set 2 TreeSet 2 1 自然排序 2 2 比较器排序 2 3 两种比较方式小结 3 HashSet 3 1 哈希值 3 2 底层原理 4 小结 1 Set Set 集合特点 可以去除重复 存取顺序不一致 没有带索引的方