HashSet集合保证元素唯一性源码分析

HashSet集合添加元素的过程:

柏维怡

HashSet集合存储元素:

  • HashSet要确保元素的唯一性,需要重写hashCode()和equal()方法

代码演示

图:
柏维怡我想你了

1. 不重写hashCode()和equal()方法下

Student类

package CCom.Cnn;

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

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        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) {
        Age = age;
    }
}

Demo类

package CCom.Cnn;

import java.util.HashSet;

/*
      HashSet集合保证元素唯一性源码分析
      HashSet集合添加元素的过程:

      HashSet要确保元素的唯一性,需要重写hashCode()和equal()方法

 */
public class Demo {
    public static void main(String[] args) {
        //建立HashSet集合
        HashSet<Student> s = new HashSet<>();
        //往HashSet集合存元素
        Student s1 = new Student("沸羊羊",21);
        Student s2 = new Student("美羊羊",22);
        Student s3 = new Student("美羊羊",22);
        s.add(s1);
        s.add(s2);
        s.add(s3);
        //查询给对象的哈希地址
        System.out.println(s.hashCode());//-714941564
        System.out.println(s1.hashCode());//460141958
        System.out.println(s2.hashCode());//1163157884
        //注意,这里不重写下判断s2和s3的!!!地址!!!!相等不
        System.out.println(s2.equals(s3));//false
        //使用foreach来循环遍历(Iterator也行),注意这里会遍历的元素会重复
        for(Student x:s){
            System.out.println(x.getName()+x.getAge());
        }


    }
}

输出的内容:
-714941564
460141958
1163157884

false

美羊羊22
美羊羊22
沸羊羊21

2. 重写hashCode()和equal()方法下

Student类注意里面重写方法了

package CCom.Cnn;

import java.util.Objects;

public class Student {
    //默认继承Object类,所以是可以重写父类方法的
       private String name;
       private int Age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        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) {
        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;
        return Age == student.Age && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, Age);
    }
}

Demo类

package CCom.Cnn;

import java.util.HashSet;

/*
      HashSet集合保证元素唯一性源码分析
      HashSet集合添加元素的过程:

      HashSet要确保元素的唯一性,需要重写hashCode()和equal()方法

 */
public class Demo {
    public static void main(String[] args) {
        //建立HashSet集合
        HashSet<Student> s = new HashSet<>();
        //往HashSet集合存元素
        Student s1 = new Student("沸羊羊",21);
        Student s2 = new Student("美羊羊",22);
        Student s3 = new Student("美羊羊",22);
        //通过调用hashCode()方法,来查出该对象的位置,相当于门牌号
        System.out.println(s.hashCode());//0
        System.out.println(s1.hashCode());//861532894
        System.out.println(s2.hashCode());//1005185097
        //判断s1和s2的哈希值相等不
        System.out.println(s2.hashCode()==s1.hashCode());//false
        //此时此刻,判断s2和s3的!!!!内容!!!!!相等不
        System.out.println(s2.equals(s3));//true
        //不需要添加s3的内容了
        s.add(s1);
        s.add(s2);

        //s.add(s3);不需要存入

        //使用foreach来循环遍历(Iterator也行),此时便利的元素不重复
        for(Student x:s){
            System.out.println(x.getName()+x.getAge());
        }
    }
}

输出的内容
0
861532894
1005185097

false
true

美羊羊22
沸羊羊21

HashSet源码分析

从源码角度分析HashSet实现原理

  1. 面试官:请问HashSet有哪些特点
    应聘者:HashSet实现自set接口,set集合中元素无序且不能重复;

  2. 面试官:那么HashSet 如何保证元素不重复?

应聘者:

  • 因为HashSet底层是基于HashMap实现的,当你new一个HashSet时候,实际上是new了一个map,执行add方法时,实际上调用map的put方法,value始终是PRESENT,所以根据HashMap的一个特性: 将一个key-value对放入HashMap中时,首先根据key的hashCode()返回值决定该Entry的存储位置,如果两个key的hash值相同,那么它们的存储位置相同。
  • 如果这个两个key的equalus比较返回true。那么新添加的Entry的value会覆盖原来的Entry的value,key不会覆盖。因此,如果向HashSet中添加一个已经存在的元素,新添加的集合元素不会覆盖原来已有的集合元素;
  1. 源码分析
    先来看一下无参的构造函数:
public HashSet() {
map = new HashMap<>();
}

很显然,当你new一个HashSet的时候,实际上是new了一个HashMap

再来看一下add方法:

private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

定义一个虚拟的Object PRESENT是向map中插入key-value对应的value,因为HashSet中只需要用到key,而HashMap是key—value键值对;所以,向map中添加键值对时,键值对的值固定是PRESENT

Set接口通常表示一个集合,其中的元素不允许重复(通过hashcode和equals函数保证),常用实现类有HashSet和TreeSet,HashSet是通过Map中的HashMap实现的,而TreeSet是通过Map中的TreeMap实现的。另外,TreeSet还实现了SortedSet接口,因此是有序的集合(集合中的元素要实现Comparable接口,并覆写Compartor函数才行)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

发热的嘤嘤怪(2003计科胜胜同学)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值