HashSet 一道经典面试题 详解!!!

//Person类重写了toString,equils和hashcode方法
class Person {
    int id;
    String name;
    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public Person() {
    }
    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        if (id != person.id) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }
    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }
}
import java.util.HashSet;

public class CollectionTest {
    public static void main(String[] args){
        HashSet set = new HashSet();
        Person p1 = new Person(1,"a");
        Person p2 = new Person(2,"b");
        set.add(p1);
        set.add(p2);//将p1,p2两个对象添加到HashSet的对象set中

        p1.name = "c";
        set.remove(p1);//将p1的属性由(1,’a‘ ) 改为 (1,’c‘ ),这时remove p1
        System.out.println(set);
        1.--------------请问此时输出--------------
        
        set.add(new Person(1,"c"));//添加一个属性为 (1,’c‘ ) 的Person类匿名对象
        System.out.println(set);
        2.--------------请问此时输出--------------
        
        set.add(new Person(1,"a"));//添加一个属性为 (1,’a‘ ) 的Person类匿名对象
        System.out.println(set);
        3.--------------请问此时输出--------------
    }
}

问:1,2,3,三处分别输出了什么

Person类提供了带参构造器,重写了toString,equilshashcode方法。

题解在底下,看完题目有了想法再看哈。

题解:

|
|
|
|
我们先公布一下正确答案:

1. [Person{id=1, name='c'}, Person{id=2, name='b'}]
2. [Person{id=1, name='c'}, Person{id=2, name='b'}, Person{id=1, name='c'}]
3. [Person{id=1, name='c'}, Person{id=2, name='b'}, Person{id=1, name='a'}, Person{id=1, name='c'}]

先看第一个输出

第一问的主要操作:

p1.name = "c"; 
set.remove(p1);`

可以看到,将p1的name属性改为"c"后,尝试删除p1,然而输出却依然存在被更改后的p1 (1,’c‘ )

  • 那我们既然成功赋值了,但却没有删除p1,且编译系统也没有报错。为什么呢?
  • 原因是因为,最初的时候,p1 (1,’a‘ )p2 (2,’b‘ ) 通过各自属性算出的哈希值,并以此确定了在数组中存放的位置,这时改变 p1 (1,’a‘ ) 的属性为 (1,’c‘ ),
  • p1的所在的位置依然是 属性被没有改变之前(1,“a”)的哈希值所指的(左红框部分),
  • 这时,我们想删除p1 (1,’c‘ ) ,HashSet类的对象 set 调用remove方法删除前,
    先算出p1已被改变的(1,“c”)这个 属性 的哈希值(右紫框部分),
    但此时 下图紫框 的位置上并不存在数据,因此remove 删了个寂寞。。。
    在这里插入图片描述

第二个输出:

第二问的主要操作:

set.add(new Person(1,"c"));

如果你理解了第一个,很容易就明白此处为什么(1,“c”)这个属性可以加入了。

  • hashSet的底层算法不会让有相同属性的对象同时存在。
  • (1,“c”)这个属性算出的哈希值在数组中所指位置上并不存在数据(上图的紫框部分
    编译器就认为并不存在相同属性,就将(1,“c”)直接加入到了数组中,没有与红框部分冲突。
    所以此时set.add(new Person(1,"c"));是可以添加进去的。

第三个输出:

第二问的主要操作:`

set.add(new Person(1,"a"));

这个地方就又有一点小难度了。

有小伙伴就会问,你这不是已经把(1,“a”)这个属性的哈希值所指的位置(左红框)占了吗,hashSet不会让有相同属性的对象同时存在的啊,怎么还能加的进去(1,“a”)这个属性???

  • 我们来理一遍它添加的流程,
  • 添加(1,“a”)这个属性(蓝框)之前,hashcode算出(1,“a”)的哈希值所指的位置(左红框)
  • 发现该位置上(红框)有值,这时,调用equils方法,返回false,表明其属性【(1,a)与(1,c)】并不相等,于是将(1,“a”)以链表形式挂载在下面。(jdk8)
    注意:数组中位置冲突元素与已经存在指定索引位置上数据以链表的方式存储
    在这里插入图片描述

注:

  1. HashSet是Set接口的实现类,所以也是存储无序的、不可重复的数据 。
  2. 底层实际上是HashMap,可以说白了,HashSet就是限制了功能的HashMap,所以了解HashMap的实现原理,这个HashSet自然就通了,对于HashSet中保存的对象,主要要正确重写equals方法和hashCode方法,以保证放入Set对象的唯一性,而本文中的Person类正是重写了这两个方法。
|----Collection接口:单列集合,用来存储一个一个的对象
  	|----List接口:存储有序的、可重复的数据。  
  		|----ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
      	|----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
     	|----Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储

    |----Set接口:存储无序的、不可重复的数据   
        |----HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
	          |----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历在添加数据的同时,
 每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
        |----TreeSet:可以照添加对象的指定属性,进行排序。

另:
深入理解HashSet(底层是HashMap)
Java集合——全

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值