一、Set集合的特点:
- java.util.Set接口 extends Collection接口
- 不允许存储重复的元素
- 没有索引,没有带索引的方法,也不能使用普通的for循环。(和Collection接口方法一样)
二、java.util.HashSet<E>
- java.util.HashSet集合 implements Set接口
- 不允许存储重复的元素
- 没有索引,没有带索引的方法,必须使用增强for循环。(和Collection接口方法一样)
- 是一个无序的集合(存储元素和取出元素的顺序可能不同)
- 底层实现是一个哈希表结构(查询速度非常快)
举例说明不允许存储重复的元素:
1 | import java.util.HashSet; |
结果分析:
1
2
3
!!!!!!!!!!!
1
2
3
(没有出现第四个重复的元素1!!!)
三、HashSet集合存储数据的结构(哈希表)
哈希值:是一个十进制的整数,由系统随机给出(对象的地址值,是一个逻辑地址,是模拟出来的地址,不是数据实际存储的物理地址)。
在Object类有一个方法可以获取对象的哈希值:hashCode();
hashCode方法的源码:
public native int hashCode();
native:代表该方法调用的是本地操作系统的方法。
1 | public class Main extends Object{ //写继承Object类更加明显看出是Object类的方法 |
toString方法的源码:
return getClass().getName()+”@”+Integer.toHexString(hashCode()); 输出为“类名@十六进制数字”
1 | public class Main extends Object { |
四、哈希表
jdk1.8版本之前:
哈希表=数组+链表
jdk1.8版本之后:
哈希表=数组+链表
哈希表+数组+红黑树(提高查询速度)
数组结构:相同哈希值的元素分为一组
链表结构//红黑树:相同哈希值的元素连接一起
步骤:
- 将数据存储到集合中,先计算元素的哈希值。
- 然后按照哈希值将其放入不同的数组中,相同的就连接在一起。
- 如果连接的元素>=8 —-链表->红黑树(一层红色结点,一层黑色结点)
五、Set集合存储元素不重复的原理
举例:
1 | import java.util.*; |
结果分析:set.add(s1);
add方法调用s1的hashCode方法 -> 计算字符串”abc”的哈希值(96354) -> 在集合中找是否有96354这个哈希值的元素 -> 没有就把s1存储到集合中
set.add(s2);
add方法调用s2的hashCode方法 -> 计算字符串”abc”的哈希值(96354) -> 在集合中找是否有96354这个哈希值的元素 -> 有就是哈希冲突(值相同,元素不同) -> 调用s2.equals(s1)返回true -> 不会将s2存储到集合中
set.add("重地");
add方法调用”重地”的hashCode方法 -> 计算字符串”重地”的哈希值(1179395) -> 在集合中找是否有1179395这个哈希值的元素 -> 没有就把”重地”存储到集合中
set.add("通话");
add方法调用”通话”的hashCode方法 -> 计算字符串”通话”的哈希值(1179395) -> 在集合中找是否有1179395这个哈希值的元素 -> 有就是哈希冲突(值相同,元素不同) -> “通话”.equals(“重地”)返回false -> 将”通话”存储到集合中
六、HashSet存储自定义类型元素
必须重写HashSet()和equals()方法。
七、LinkedHashSet<E>
LinkedHashSet集合(具有可预知的迭代顺序) extends HashSet集合(具有可预知的迭代顺序)
- 底层实现是一个哈希表结构(查询速度非常快)
- 哈希表=数组+链表/红黑树+链表(记录元素的存储顺序)
例子:
1 | import java.util.*; |