【数据结构】HashSet的底层数据结构

简介: 【数据结构】HashSet的底层数据结构

一、 HashSet 集合的底层数据结构

  • HashSet :无序、不重复、无索引
  • HashSet 底层是采用哈希表存储数据的,哈希表是一种对于增删改查数据性能都较好的结构
  • 哈希表在JDK8之前是由数组+链表组成的,在JDK8之后是由数组+链表+红黑树组成的
  • 在哈希表中,最重要的是哈希值,哈希值就是对象的整数表现形式,HashSet 在存数据的时候,会根据数组长度和哈希值计算出要存入的位置,哈希值是根据hashCode()方法计算出来的int型的整数,hashCode()方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算,一般情况下,自定义的对象都要重写hashCode()方法,利用对象内部的属性值计算哈希值。
int index = (数组长度 - 1) & 哈希值;
  • 对象的哈希值特点:
  • 如果没有重写hashCode()方法,同一个类创建的不同对象计算出的哈希值是不同的
public class Student {
    private String name;
    private int age;
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }
    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }
    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
public static void main(String[] args) {
        //创建对象
        //没有重写hashCode方法,计算出的哈希值是不同的
        Student s1 = new Student();
        Student s2 = new Student();
        System.out.println(s1.hashCode());//460141958
        System.out.println(s2.hashCode());//1163157884
    }

  • 如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
public class Student {
    private String name;
    private int age;
    @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 && Objects.equals(name, student.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }
    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }
    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }
    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
public static void main(String[] args) {
        //创建对象
        //如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
        Student s1 = new Student();
        Student s2 = new Student();
        System.out.println(s1.hashCode());//961
        System.out.println(s2.hashCode());//961
    }

  • 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)
public static void main(String[] args) {
        //在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)
        System.out.println("abc".hashCode());//96354
        System.out.println("acD".hashCode());//96354
    }

二、 HashSet 添加元素的过程

HashSet在JDK8以后的底层原理:

  • 创建一个默认长度为16,默认加载因子为0.75的数组,数组名为table
  • 根据元素的哈希值跟数组长度计算处应存入的位置
int index = (数组长度 - 1) & 哈希值;
  • 判断当前位置是否为null,如果是null,则直接存入
  • 如果当前位置不是null,表示有元素,则调用equals()方法与当前位置的属性进行比较
  • 如果相同,则舍弃不存
  • 如果不同,则存入数组,形成链表
  • JDK8以前,新元素存入数组,老元素挂在新元素下面形成链表
  • JDK8之后,新元素挂在老元素下面形成链表
  • 当链表长度大于8且数组长度大于等于64时,当前链表会自动转成红黑树
  • 如果集合中存储的是自定义对象,必须重写 hashCode 和 equals 方法

三、 HashSet 为什么存和取的顺序不一样

HashSet 在遍历的时候是从数组的0索引开始遍历的,每个索引下都要遍历该索引下对应的链表,当遍历到一个索引,这个索引的值为空时,会跳过,遍历下一个索引,该索引下对应有链表时,就会遍历这个链表,若是红黑树,也会遍历这个红黑树,按这个原则遍历数组,因为某个索引下对应的元素不一定就是存入时的顺序,所以HashSet 在存和取时的顺序也不一定相同。



四、 HashSet 为什么没有索引

HashSet 是由数组+链表+红黑树组成的,数组是有索引的,但是存在HashSet 中的元素是通过链表或红黑树的形式挂在数组的每个索引下的,也就是每个索引可能对应多个元素,所以HashSet 不能由索引找到对应的元素。



五、 HashSet 的去重机制

HashSet 是通过HashCode计算出每个元素应该存放的位置,,然后通过equals方法去比较对象内部的属性值是否一致,保证不会出现重复的元素。

相关文章
|
Java
【JAVA数据结构】哈希表-HashSet and HashMap(二)
JAVA数据结构 & 哈希表 -HashSet and HashMap
62 0
|
Java
【JAVA数据结构】哈希表-HashSet and HashMap
JAVA数据结构 & 哈希表 -HashSet and HashMap
48 0
|
存储 Java 程序员
面试官:HashSet 的实现原理是怎样的?底层是什么数据结构?被问到了。。
面试官:HashSet 的实现原理是怎样的?底层是什么数据结构?被问到了。。
466 0
面试官:HashSet 的实现原理是怎样的?底层是什么数据结构?被问到了。。
|
存储 安全 Java
面试宝典:数据结构-HashSet
面试宝典:数据结构-HashSet
面试宝典:数据结构-HashSet
|
算法 Java
《恋上数据结构第1季》集合 ListSet、TreeSet、HashSet
《恋上数据结构第1季》集合 ListSet、TreeSet、HashSet
|
11天前
数据结构——栈和队列
数据结构——栈和队列
13 1
|
21小时前
|
存储 测试技术
【数据结构】操作受限的线性表,栈的具体实现
【数据结构】操作受限的线性表,栈的具体实现
12 5
|
1天前
|
算法
【C/数据结构和算法】:栈和队列
【C/数据结构和算法】:栈和队列
6 1
|
5天前
|
C++
【洛谷 P1044】[NOIP2003 普及组] 栈 题解(递归+记忆化搜索)
**NOIP2003普及组栈问题**:给定操作数序列1到n,仅允许push(进栈)和pop(出栈)操作。目标是计算所有可能的输出序列总数。输入包含一个整数n(1≤n≤18)。示例输入3,输出5。当队列空时返回1,栈空则只能入栈,栈非空时可入栈或出栈。AC C++代码利用记忆化搜索求解。
8 1
|
7天前
|
算法
$停车场管理系统 栈与队列
$停车场管理系统 栈与队列
8 1