HashSet去重原理
首先我们来说说HashSet的特点:
- 它是无序的,即添加的顺序和遍历出来的顺序是不同的
- 它里面不允许有重复元素,是因为它是基于HashMap实现的
- 实现了Set接口,由哈希表(实际上是一个HashMap实例)支持
- 底层数据结构是哈希表
那它是如何保证元素的唯一性的呢?
答案是依赖两个方法:
hashCode()和equals()方法
如果我们希望一个集合有去重复的功能, 可以在它的add方法中检查要添加的对象在集合中是否存在,迭代集合中每个元素, 和要添加的比较, 如果相同, 就不存,所以我们来看看add()方法的部分源码解析:
因为源码太长,我就截取一小部分重要的出来哈!
private static final Object PRESENT = new Object();
private transient HashMap<E,Object> map;
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
......
if (e.hash == hash && ((k = e.key) == key || key.equals(k))){
......//e.hash调用了hashCode()方法获得了e元素的hash值
}
从源码中我们可以看出:
它的add()方法实际上调用的是HashMap中的put()方法,把要添加进HashSet中的元素当做key存入,而value则是一个固定值:一个Object类对象。
先用hashCode()方法获得传入元素的哈希值,在集合中查找是否包含哈希值相同的元素,如果相同,则继续进行比较它们地址值,一般地址值都是不相同的,所以最后会用equals()方法比较对象内的属性值。
比较结果全为false就存入,如果比较结果有true则不存.
那如何将自定义类对象存入HashSet进行去重呢?
- 类中必须重写hashCode()方法和equals()方法
- equals()方法中比较所有属性
- hashCode()方法要保证属性相同的对象返回值相同, 属性不同的对象尽量不同,对象的成员变量值相同即为同一个元素
我用了一个Student类进行说明,类中有name和age属性:
>重写后的hashCode()方法:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
要尽可能的让不同对象的哈希值不相同,因为使用equals()方法进行一系列的判断的前提是要建立在哈希值相同的基础上的,如果哈希值不相同,就直接添加该元素,就不用进行后续的判断了,这样就节省了好多资源;
那怎样保证hash值尽量不相同呢?
由重写后的hashCode()方法可以看出:成员变量的值影响了哈希值,所以我们把成员变量的值和哈希值做一系列的运算,更改后相加即可,例如我们可以把它们乘以一些整数
重写后的equals()方法:
@Override
public boolean equals(Object obj) {
if (this == obj) //先判断传入的对象地址是否和当前对象一样
return true; //如果一样的话,肯定为同一对象,直接返回true
if (obj == null) //如果传入的对象为空的话
return false; //则直接返回false,添加到集合中,因为Set可以存null值
if (getClass() != obj.getClass())//然后判断字节码文件的对象
return false;//不相等的话说明两个元素肯定不同,就直接返回false
Student other = (Student) obj; //然后进行成员变量的比较
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
HashSet去重原理总结:
A:HashSet底层数据结构是哈希表(是一个元素为链表的数组)
B:哈希表底层依赖两个方法:hashCode()和equals()
执行顺序:
首先比较哈希值是否相同
相同:继续执行equals()方法
返回true:元素重复了,不添加
返回false:直接把元素添加到集合
不同:就直接把元素添加到集合
C:元素唯一性由hashCode()和equals()保证的,二者缺一不可