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



目录
相关文章
|
5天前
|
存储 JavaScript 前端开发
JavaScript进阶-Map与Set集合
【6月更文挑战第20天】JavaScript的ES6引入了`Map`和`Set`,它们是高效处理集合数据的工具。`Map`允许任何类型的键,提供唯一键值对;`Set`存储唯一值。使用`Map`时,注意键可以非字符串,用`has`检查键存在。`Set`常用于数组去重,如`[...new Set(array)]`。了解它们的高级应用,如结构转换和高效查询,能提升代码质量。别忘了`WeakMap`用于弱引用键,防止内存泄漏。实践使用以加深理解。
|
13天前
|
存储 安全 Java
Java集合详解:Set, Map, Vector, List的对比与联系
Java集合框架核心包括List、Set、Map和Vector。List允许重复元素,如ArrayList(适合读取)和LinkedList(适合插入删除)。Set不允许重复,有HashSet(无序)和TreeSet(排序)。Map存储键值对,HashMap(无序)和TreeMap(排序)。Vector是线程安全的ArrayList替代品,但在多线程环境下使用。选择集合类型应根据应用场景,如有序、无序、键值对需求及线程安全考虑。
|
16天前
|
存储 安全 Java
Java 集合(List、Set、Map 等)相关问答归纳再整理
HashMap 中使用键对象来计算 hashcode 值 HashSet 使用成员对象来计算 hashcode 值,对于两个对象来说hashcode 可能相同,所以 equals() 方法用来判断对象的相等性,如果两个对象不同的话,那么返回 false。 HashMap 比较快,因为是使用唯一的键来获取对象,HashSet 较 HashMap 来说比较慢。 4.1.3 HashMap 与 TreeMap
10 2
|
18天前
|
存储 Java 索引
JavaSE——集合框架一(6/7)-Set系列集合:LinkedHashSet的底层原理、TreeSet集合(介绍,自定义排序规则,排序示例)
JavaSE——集合框架一(6/7)-Set系列集合:LinkedHashSet的底层原理、TreeSet集合(介绍,自定义排序规则,排序示例)
18 1
|
1月前
|
C++ 容器
【C++】红黑树模拟实现STL中的map与set
【C++】红黑树模拟实现STL中的map与set
|
1月前
|
算法 测试技术 C++
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(下)
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(下)
|
1月前
|
C++ 容器
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(上)
【C++】map&set的底层结构 -- AVL树(高度平衡二叉搜索树)(上)
|
4天前
|
存储 算法 NoSQL
C++一分钟之-map与set容器详解
【6月更文挑战第21天】C++ STL的`map`和`set`是基于红黑树的关联容器,提供有序存储和高效查找。`map`存储键值对,键唯一,值可重复;`set`仅存储唯一键。两者操作时间复杂度为O(log n)。常见问题包括键的唯一性和迭代器稳定性。自定义比较函数可用于定制排序规则,内存管理需注意适时释放。理解和善用这些工具能提升代码效率。
10 3
|
6天前
|
存储 编译器 C++
|
27天前
|
C语言
从C语言到C++_29(红黑树封装set和map)红黑树迭代器的实现(下)
从C语言到C++_29(红黑树封装set和map)红黑树迭代器的实现
27 3