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



目录
相关文章
|
2月前
|
存储 NoSQL 关系型数据库
Redis 集合(Set)
10月更文挑战第17天
42 5
|
2月前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
37 2
|
1月前
set集合
HashSet(无序,唯一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素。 LinkedHashSet: LinkedHashSet 是 HashSet 的子类,并且其内部是通过 LinkedHashMap 来实现的。 TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树)。
|
1月前
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
|
1月前
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
|
1月前
|
Java 开发者
|
2月前
|
Java 开发者
在Java集合世界中,Set以其独特的特性脱颖而出,专门应对重复元素
在Java集合世界中,Set以其独特的特性脱颖而出,专门应对重复元素。通过哈希表和红黑树两种模式,Set能够高效地识别并拒绝重复元素的入侵,确保集合的纯净。无论是HashSet还是TreeSet,都能在不同的场景下发挥出色的表现,成为开发者手中的利器。
28 2
|
17天前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
52 18
你对Collection中Set、List、Map理解?
|
10天前
|
存储 缓存 安全
只会“有序无序”?面试官嫌弃的List、Set、Map回答!
小米,一位热衷于技术分享的程序员,通过与朋友小林的对话,详细解析了Java面试中常见的List、Set、Map三者之间的区别,不仅涵盖了它们的基本特性,还深入探讨了各自的实现原理及应用场景,帮助面试者更好地准备相关问题。
48 20
|
27天前
|
存储 C++ 容器
【C++】map、set基本用法
本文介绍了C++ STL中的`map`和`set`两种关联容器。`map`用于存储键值对,每个键唯一;而`set`存储唯一元素,不包含值。两者均基于红黑树实现,支持高效的查找、插入和删除操作。文中详细列举了它们的构造方法、迭代器、容量检查、元素修改等常用接口,并简要对比了`map`与`set`的主要差异。此外,还介绍了允许重复元素的`multiset`和`multimap`。
30 3
【C++】map、set基本用法