一、概述
java.util.HashSet 是 Set 接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。
java.util.HashSet 底层的实现是一个 java.util.HashMap 支持。
HashSet 是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存储区和查找性能。保证元素唯一性的方式依赖于:hashCode 与 equals 方法。
特点:
1. 不允许存储重复的元素
2. 没有索引,也没有带索引的方法,不能使用普通 for 循环遍历
3. 是一个无序的集合,存储元素和取出元素的顺序有可能不一致
4. 底层是一个哈希表结构(查询的速度非常的快)
二、HashSet 集合存储数据的结构
1、哈希值
哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到地址,不是数据实际存储的物理地址)
在 Object 类中有一个方法,可以获取对象的哈希值。
int hashCode() 返回该对象的哈希码值。
hashCode 方法的源码:
public native int hashCode();
native:代表该方法调用的是本地操作系统的方法
2、哈希表
在 JDK1.8 之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。
JDK1.8中(之后),哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
存储流程图:
Demo:
JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。
3、Set 集合不允许存储重复元素的原理
三、HashSet 存储自定义类型元素
给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一。
Demo:
1 // 创建自定义 student 类
2 public class Student {
3 private String name;
4 private int age;
5
6 public Student() {
7 }
8
9 public Student(String name, int age) {
10 this.name = name;
11 this.age = age;
12 }
13
14 public String getName() {
15 return name;
16 }
17
18 public void setName(String name) {
19 this.name = name;
20 }
21
22 public int getAge() {
23 return age;
24 }
25
26 public void setAge(int age) {
27 this.age = age;
28 }
29
30 @Override
31 public boolean equals(Object o) {
32 if (this == o)
33 return true;
34 if (o == null || getClass() != o.getClass())
35 return false;
36 Student student = (Student) o;
37 return age == student.age &&
38 Objects.equals(name, student.name);
39 }
40
41 @Override
42 public int hashCode() {
43 return Objects.hash(name, age);
44 }
45 }
46 // 创建测试类
47 public class HashSetDemo2 {
48 public static void main(String[] args) {
49 //创建集合对象 该集合中存储 Student类型对象
50 HashSet<Student> stuSet = new HashSet<Student>();
51 //存储
52 Student stu = new Student("于谦", 43);
53 stuSet.add(stu);
54 stuSet.add(new Student("郭德纲", 44));
55 stuSet.add(new Student("于谦", 43));
56 stuSet.add(new Student("郭麒麟", 23));
57 stuSet.add(stu);
58
59 for (Student stu2 : stuSet) {
60 System.out.println(stu2);
61 }
62 }
63 }
64 执行结果:
65 Student [name=郭德纲, age=44]
66 Student [name=于谦, age=43]
67 Student [name=郭麒麟, age=23]
View Code
四、遍历集合
1、增强 for 循环
for (Integer i : set) {
System.out.println(i);
}
2、迭代器遍历
Iterator<Integer> it = set.iterator();
while (it.hasNext()){
Integer n = it.next();
System.out.println(n);
}