Java 集合 - HashSet
HashSet 是一个没有重复元素的集合,它其实是由 HashMap 实现的,HashMap 保存的是建值对,然而我们只能向 HashSet 中添加 Key,原因在于 HashSet 的 Value 其实都是同一个对象,这是 HashSet 添加元素的方法,可以看到辅助实现 HashSet 的 Map 中的 Value 其实都是 Object 类的同一个对象。
特点:
- 底层数据结构是哈希表
- 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,所以不能使用普通 for 循环遍历
- 由于是 Set 集合,所以是不包含重复元素的集合
# 存储规则
哈希表边存放的是哈希值。HashSet 存储元素的顺序并不是按照存入时的顺序(和 List 显然不同)是按照哈希值来存的所以取数据也是按照哈希值取得。
HashSet 不存入重复元素的规则.使用 hashcode 和 equals。
由于 Set 集合是不能存入重复元素的集合。那么 HashSet 也是具备这一特性的。HashSet 如何检查重复?HashSet 会通过元素的 hashcode 和 equals 方法进行判断元素是否重复。
当你试图把对象加入 HashSet 时,HashSet 会使用对象的 hashCode 来判断对象加入的位置。同时也会与其他已经加入的对象的 hashCode 进行比较,如果没有相等的 hashCode,HashSet 就会假设对象没有重复出现。
简单一句话,如果对象的 hashCode 值是不同的,那么 HashSet 会认为对象是不可能相等的。
因此我们自定义类的时候需要重写 hashCode,来确保对象具有相同的 hashCode 值,因为 new 的两个类数据一样,这两个类默认 hashCode 不一致,所以主动重写 hashCode,通过对比类的数据来判断哈希值是否一样。
如果元素(对象)的 hashCode 值相同,是不是就无法存入 HashSet 中了?当然不是,会继续使用 equals 进行比较。如果 equals 为 true,那么 HashSet 认为新加入的对象重复了,所以加入失败。如果 equals 为 false 那么 HashSet 认为新加入的对象没有重复,新元素可以存入。
总结:
元素的哈希值是通过元素的 hashcode 方法来获取的, HashSet 首先判断两个元素的哈希值,如果哈希值一样,接着会比较 equals 方法 如果 equls 结果为 true,HashSet 就视为同一个元素。如果 equals 为 false 就不是同一个元素。
哈希值相同 equals 为 false 的元素是怎么存储呢,就是在同样的哈希值下顺延(可以认为哈希值相同的元素放在一个哈希桶中)。也就是哈希一样的存一列。
# HashSet用处
问题:现在有一批数据,要求不能重复存储元素,而且要排序。ArrayList、LinkedList 不能去除重复数据。HashSet 可以去除重复,但是是无序。
所以这时候就要使用 TreeSet 了
案例:创建一个学生类,并重写 equals 和 hashcode
import java.util.Objects;
public class HashSetStudent {
public String name;
public int age;
public HashSetStudent(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HashSetStudent that = (HashSetStudent) o;
return age == that.age && Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
代码:
public class HashSetTest {
public static void main(String[] args) {
HashSetStudent s1 = new HashSetStudent("李白1",15);
HashSetStudent s2 = new HashSetStudent("李白2",16);
HashSetStudent s3 = new HashSetStudent("李白1",15); // 重复值不插入
HashSetStudent s4 = new HashSetStudent("李白1",10);
HashSetStudent s5 = new HashSetStudent("李白2",10);
HashSet<HashSetStudent> hs = new HashSet<>();
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
hs.add(s5);
for(HashSetStudent S:hs){
System.out.println(S.name+"--"+S.age);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
最后输出:
李白2--10 李白1--10 李白1--15 李白2--16
# LinkedHashSet
特点:
- 哈希表和链表实现的 Set 接口,具有可预测的迭代次序
- 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
- 由哈希表保证元素唯一,也就是说没有重复的元素