写在前面
- 由于个人水平有限,故部分内容可能会出现错误,还请包涵
Set 接口
Set 也是一种用于存储元素的集合,但 Set 内的元素是无序且不重复的
Set 中没有特有的方法,直接继承自 Collection
相等性
引用到堆上同一个对象的两个引用是相等的。如果对两个引用调用 hashCode 方法,会得到相同的结果
如果对象所属的类没有覆盖 Object 的 hashCode方法 的话,hashCode 会返回每个对象特有的序号(Java 是依据对象的内存地址计算出的此序号),所以两个不同的对象的 hashCode 值是不可能相等的
hashCode 和 equals 方法的区别和联系
hashCode 和 equals 方法均在 Object 类中定义
equals (Object obj) 方法用来判断两个对象是否“相同”,如果“相同”则返回 true,否则返回 false。hashCode() 方法返回一个 int 数,在 Object 类中的默认实现是“将该对象的内部地址转换成一个整数返回”
若重写了 equals(Object obj) 方法,则有必要重写 hashCode() 方法
- 一般一个类的对象如果会存储在 HashTable,HashSet,HashMap 等散列存储结构中,那么重写 equals 后最好也重写 hashCode,否则会导致存储数据的不唯一性(存储了两个 equals 相等的数据)
若 equals 返回 true,则 hashCode 返回的 int 值相等。但若 hashCode 返回的 int 值相等,equals 返回不一定为 true
hashCode 是为了提高在散列结构存储中查找的效率,在线性表中没有作用
同一对象在执行期间若已经存储在集合中,则不能修改影响 hashCode 值的相关信息,否则会导致内存泄露问题
如果想要让类的两个不同对象视为相等,就必须重写 Object 中的 hashCode 方法和 equals 方法
HashSet 类
1 | public class HashSet<E> |
HashSet 类的底层数据结构是 HashMap,即 HashSet 类的所有方法实际上都是通过 HashMap 类的方法实现的,故在此不作深究,具体可参考 Java Map 解析1
2// 默认初始容量为 16,负载因子为 0.75
private transient HashMap<E,Object> map;
而因为 Map 存储的是键-值对的元素,故 HashSet 类内部包含了一个类实例 PRESENT ,即 map 中的所有 value 都是这个 PRESENT1
private static final Object PRESENT = new Object();
HashSet 的 clone 方法同样是浅拷贝
LinkedHashSet 类
1 | public class LinkedHashSet<E> |
LinkedHashSet 类的底层数据结构是 LinkedHashMap,具体可参考 Java Map 解析
LinkedHashSet 的构造函数都是通过调用 HashSet 的一个具有包访问权限的构造函数实现的
LinkedHashSet 的好处在于可以保证访问元素的顺序和插入元素的顺序相同
具体使用
1 | Set<String> set = new HashSet<>(Arrays.asList("Java", "Python", "C", "PHP")); |
输出结果为1
2
3
4
5
6
7
8Java
C
PHP
Python
Java
Python
C
PHP
TreeSet 类
1 | public class TreeSet<E> extends AbstractSet<E> |
TreeSet 类的底层数据结构是 TreeMap,具体可参考 Java Map 解析
TreeSet 的有点在于可以确保集合元素处于排序状态(排序规则可以自定义)
总结
Set 源码阅读十分简单,因为 Set 实现类的底层数据结构都是相应的 Map 实现类,故重点全放在 Map 源码中
作为 key 的对象要正确重写 equals 和 hashCode 方法
Set 更像一个功能被限制了的 Map