HashSet经典面试题
基本特点
-
唯一性:
HashSet
中的元素不能重复,这是通过HashMap
的键(key
)的唯一性实现的。 -
无序性:
HashSet
不保证元素的存储顺序,因为它的底层是基于哈希表的。 -
线程不安全:
HashSet
是非线程安全的,若需要线程安全版本,可以使用Collections.synchronizedSet()
或ConcurrentHashMap
相关实现。
前置知识:
public class test01 {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
Student stu_1 = new Student("张三", 18);
set.add(stu_1);
Student stu_2 = new Student("张三", 18);
set.add(stu_2);
Iterator<Student> it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next().toString());
}
}
}
根据hashset无序不重复的特性,即使引用地址不同,仍然能够控制元素不重复。
问题1:
下面的remove是否能够成功移除stu_1对象?
public class test01 {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
Student stu_1 = new Student("张三", 18);
set.add(stu_1);
stu_1.setName("李四");
set.remove(stu_1); //执行一次删除
Iterator<Student> it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next().toString());
}
}
}
移除操作的核心在于,存储在HashSet
中的对象是否正确重写 hashCode()
方法。
代码执行结果显示,stu_1对象并没有被删除,remove操作失败:
代码运行的逻辑
1. HashSet
添加元素
- 当你调用
set.add(stu_1)
时,HashSet
会根据stu_1
的hashCode()
计算出一个哈希值,找到对应的桶(bucket),并将元素添加到该位置。 - 如果没有自定义
hashCode
方法,stu_1
的哈希值来自Object
类的hashCode()
方法,默认基于对象内存地址生成。
2. 修改对象后
- 调用了
stu_1.setName("李四")
修改了对象的状态。如果hashCode()
是基于对象的属性(如name
和age
)计算的,那么hashCode
的值就会发生变化。 HashSet
中的哈希表是静态的,修改对象不会触发重新计算哈希值,因此哈希表的结构没有变化。此时,HashSet
的数据结构已经失效,stu_1
在HashSet
中变成了“找不到”的元素。
3. 调用 remove
方法
- 当你调用
set.remove(stu_1)
时,HashSet
会用当前stu_1
的hashCode
值去哈希表中寻找对应的桶。 - 因为
stu_1
的hashCode
已经改变,HashSet
找不到之前存储的位置,所以删除失败。
问题2:
重写hashcode()方法的前提下,stu_2能否添加进hashset集合中?
public class test01 {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
Student stu_1 = new Student("张三", 18);
set.add(stu_1);
stu_1.setName("李四");
set.remove(stu_1);
//新增代码是否生效?
Student stu_2 = new Student("李四", 18);
set.add(stu_2);
Iterator<Student> it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next().toString());
}
}
}
可以添加成功,因为第一次set.add(stu_1)
时,HashSet
会根据 stu_1
的“张三”和18来计算哈希值,而这一次添加对象,却是根据李四和18计算出来的。
问题3:
stu_3能否添加成功?
public class test01 {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
Student stu_1 = new Student("张三", 18);
set.add(stu_1);
stu_1.setName("李四");
set.remove(stu_1);
Student stu_2 = new Student("李四", 18);
set.add(stu_2);
//新增代码是否生效?
Student stu_3 = new Student("张三", 18);
set.add(stu_3);
Iterator<Student> it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next().toString());
}
}
}
虽然hashcode得到的哈希码是一样的值,找到的桶是一样的,但是equals方法结果为false,因此哈希碰撞的结果是添加进单链的后面。