JavaSE——集合框架一(5/7)-Set系列集合:Set集合的特点、底层原理、哈希表、去重复原理

简介: JavaSE——集合框架一(5/7)-Set系列集合:Set集合的特点、底层原理、哈希表、去重复原理

Set集合的特点


Set系列集合特点无序:添加数据的顺序和获取出的数据顺序不一致;不重复;无索引;

  • HashSet:无序、不重复、无索引。
  • LinkedHashSet有序、不重复、无索引。
  • TreeSet排序、不重复、无索引。
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
 
public class SetDemo1 {
    public static void main(String[] args){
        //1.创建一个Set集合的对象
//        Set<Integer> set = new HashSet<>();         //创建了一个HashSet的集合对象   经典代码  HashSet:无序不重复无索引
//        Set<Integer> set = new LinkedHashSet<>();   //有序 不重复 无索引
        Set<Integer> set = new TreeSet<>();
        set.add(666);
        set.add(555);
        set.add(555);
        set.add(888);
        set.add(888);
        set.add(777);
        set.add(777);
        System.out.println(set);
    }
}

运行结果:


注意:

Set要用到的常用方法,基本上就是collection提供的。

自己几乎没有额外新增一些常用功能。

HashSet集合的底层原理

哈希值

  • 就是一个int类型的数值Java中每个对象都有一个哈希值
  • Java中的所有对象,都可以调用Obejct类提供的hashcode方法,返回该对象自己的哈希值。

public int hashCode():返回对象的哈希码值。

对象哈希值的特点

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
  • 不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。

(int的范围:-21亿多~21亿多)

我们用代码演示一下

先定义一个学生类:


public class SetDemo2 {
    public static void main(String[] args) {
        /*
        Java中的所有对象,都可以调用Object类提供的hashCode方法,返回该对象自己的哈希值
            public int hashCode():  返回对象的哈希值.
        同一个对象多次调用hashCode()方法返回的哈希值是相同的.
        不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)
         */
        Student s1 = new Student("蜘蛛精",25,169.5);
        Student s2 = new Student("紫霞",22,166.5);
        System.out.println("第一次取s1的哈希值: " + s1.hashCode());
        System.out.println("第二次取s1的哈希值: " + s1.hashCode());
        System.out.println("第一次取s2的哈希值: " + s2.hashCode());
        System.out.println("第二次取s2的哈希值: " + s2.hashCode());
 
        System.out.println();
 
        //哈希碰撞
        String str1 = new String("abc");
        String str2 = new String("acD");
        System.out.println("str1的哈希值: " + str1.hashCode());
        System.out.println("str2的哈希值: " + str2.hashCode());
    }
}

运行结果:



HashSet集合的底层原理

  • 基于哈希表实现。
  • 哈希表是一种增删查改的数据性能都较好的数据结构。

哈希表


JDK8之前,哈希表=数组+链表

JDK8开始,哈希表=数组+链表+红黑树

JDK8之前HashSet集合的底层原理,基于哈希表:数组+链表


  1. 创建一个默认长度16的数组,默认加载因子为0.75,数组名table
  2. 使用元素的哈希值对数组的长度求余计算出应存入的位置
  3. 判断当前位置是否为null,如果是null直接存入
  4. 如果不为null,表示有元素,则调用equals方法比较。相等,则不存;不相等,则存入数组

JDK8之前,新元素存入数组,占老元素位置,老元素挂下面(也就是形成一个链表)


JDK8开始HashSet集合的底层原理,基于哈希表:数组+链表+红黑树

  • JDK8开始后,哈希表中引入了红黑树,进一步提高了操作数据的性能。

当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树



去重复原理

HashSet集合默认不能对内容一样的两个不同对象去重复。

比如内容一样的两个学生对象存入到HashSet集合中去,HashSet集合是不能去重复的

如何让HashSet集合能够实现对内容一样的两个不同对象也能去重复?

先了解一下去重复机制的原理:


  1. HashSet首先会根据hashCode()方法计算出的哈希值找到元素应该存放的位置。
  2. 位置中如果没有元素则直接存放,若已有元素,则再使用equals()方法对该位置的元素一一进行比较。
  3. 当equals()方法返回true时就不进行存储,从而起到了去重复的效果。

所以,得到结论:

  • 如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法。

实例演示

import java.util.HashSet;
import java.util.Set;
 
public class SetDemo3 {
    public static void main(String[] args) {
        Set<Student> students = new HashSet<>();
        Student s1 = new Student("至尊宝",28,169.6);
        Student s2 = new Student("蜘蛛精",23,169.6);
        Student s3 = new Student("蜘蛛精",23,169.6);
        System.out.println("s2的哈希值: " + s2.hashCode());
        System.out.println("s3的哈希值: " + s3.hashCode());
        Student s4 = new Student("牛魔王",48,169.6);
        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);
        System.out.println(students);
    }
}

先看主程序,没有重写对象hashCode()方法和equals()方法的运行结果:



重写对象hashCode()方法和equals()方法

import java.util.Objects;
 
public class Student {
    private String name;
    private int age;
    private double height;
 
    public Student() {
    }
 
    public Student(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
 
    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;
    }
 
    public double getHeight() {
        return height;
    }
 
    public void setHeight(double height) {
        this.height = height;
    }
 
    @Override
    public String toString() {
        return '\n' + "名字:" + name + '\n' +
                "年龄:" + age + '\n' +
                "身高:" + height + '\n';
    }
 
    //只要两个对象内容一样,就返回true
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Double.compare(student.height, height) == 0 && Objects.equals(name, student.name);
    }
 
    //只要两个对象内容一样,返回的哈希值就一样
    @Override
    public int hashCode() {
        //根据名字 身高 年龄来计算哈希值
        return Objects.hash(name, age, height);
    }
}

重写完之后我们就可以重新看一下运行结果:



可以看到,返回的哈希值是一样的,并且内容重复的两个不同对象也没有都加入到集合中


END



目录
相关文章
|
存储 NoSQL 关系型数据库
Redis 集合(Set)
10月更文挑战第17天
176 5
|
8月前
|
存储 算法 PHP
数组去重性能优化:为什么Set和Object哈希表的效率最高
在处理数组去重问题时,使用 `Set` 和 `Object` 哈希表是高效的解决方案。它们基于哈希表实现,插入和查找操作的时间复杂度为 `O(1)`,相比传统嵌套循环的 `O(n²)` 方法性能优势显著。`Set` 能保持元素插入顺序,适用于需要顺序的场景;`Object` 则通过键的唯一性实现去重,适合无需顺序的场景。两者均能在大规模数据中实现高效的去重操作,是数组去重最优选择。
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
205 2
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
4603 113
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
1123 113
|
编译器 容器
哈希表模拟封装unordered_map和unordered_set
哈希表模拟封装unordered_map和unordered_set
set集合
HashSet(无序,唯一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素。 LinkedHashSet: LinkedHashSet 是 HashSet 的子类,并且其内部是通过 LinkedHashMap 来实现的。 TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树)。
|
5月前
|
存储 JavaScript Java
(Python基础)新时代语言!一起学习Python吧!(四):dict字典和set类型;切片类型、列表生成式;map和reduce迭代器;filter过滤函数、sorted排序函数;lambda函数
dict字典 Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。 我们可以通过声明JS对象一样的方式声明dict
356 1
|
8月前
|
存储 缓存 JavaScript
Set和Map有什么区别?
Set和Map有什么区别?
585 1