相关定义
作为Collection接口的重要子接口,Set接口是一个不包含重复元素,且元素排列无序的集合,也被称为集。
注意:不包含重复元素的含义,更确切的讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。
Set接口的方法和Collection接口的方法大体相同,也就是说Set接口不像List接口那样可以通过索引或去元素,只能通过Iterator()方法获取迭代器进行迭代。
Set中的add方法不同于Collection接口,有一个boolean类型的返回值,当集合中含有与某个元素equals相等的元素时返回false,无法添加,否则返回true。
Set中的Iterator方法返回的返回的元素没有特定的顺序(除非此 set 是某个提供顺序保证的类的实例)。
HashSet
HashSet类是Set接口的重要实现类,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。
此类为基本操作提供了稳定性能,这些基本操作包括 add、remove、contains 和 size,假定哈希函数将这些元素正确地分布在桶中。对此 set 进行迭代所需的时间与 HashSet 实例的大小(元素的数量)和底层 HashMap 实例(桶的数量)的“容量”的和成比例。因此,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。
注意,此实现不是同步的。
另外,Set接口是通过equals方法来比较两元素是否相同的,但是如果有1000个元素,那么再添加一个元素,就要比较1000次,效率严重降低。所以HashSet类底层采用了HashMap来存储元素,而HashMap采用了哈希表的原理。
因此HashSet类按照哈希算法来存取对象,当向集合中加入一个新对象时,会调用对象的HashCode()方法得到对象的哈希码,然后根据这个码计算出对象在集合中存储的位置。 HashCode()方法实际上是返回的对象存储的物理地址。具体解释请参看博客Java基础之hashCode方法详解
所以总的来说,HashSet是通过两元素的HashCode()方法和equals()方法来判断两元素是否相等的。
具体的判断顺序为:
- 判断两个对象的hashCode是否相等。如果不相等,认为两个对象也不相等,完毕。如果相等转入2
- 判断两个对象用equals方法运算是否相等。如果不相等,认为两个对象也不相等。如果相等,认为两个对象相等。
之所以还要进行一次equals方法进行比较,是因为hashCode方法相等时,equals方法可能不相等。
所以当我们使用HashSet添加元素时一定要重写该元素的HashCode方法和equals方法。并且,参与计算两个方法返回值的关键属性应该相同。
例子:
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetText {
public static void main(String[] args) {
// TODO Auto-generated method stub
Set<Person> set = new HashSet<>();
set.add(new SetText().new Person("sjs"));
set.add(new SetText().new Person("sja"));
Iterator<Person> iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
class Person{
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (Person.class == obj.getClass()) {
Person p = (Person)obj;
return this.name.equals(p.getName());
}
return false;
}
@Override
public String toString() {
return this.name;
}
}
}
运行结果:
sjs
sja
重点掌握HashCode和equals方法的重写办法。