Set 不能有重复的元素,且是无序的,要有空值也就只能有一个,因为它不允许重复。
Set接口中的方法和Collection一致。
HashSet
HashSet 内部数据结构是哈希表 ,是不同步的。
- 如何保证该集合的元素唯一性呢?
是通过对象的hashCode()和equals()方法来保证对象唯一性的。 - 如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中。
- 如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true。
如果为true,视为相同元素,不存。
如果为false,那么视为不同元素,就进行存储。
- 如果hashcode函数返回一个定值,即所有对象的hashcode相同时,当尝试插入第二个数据(或以上)时,会发现程序还会去调用equals方法,并且该函数返回false时才回插入HashSet;当hashcode返回不同时会忽略equals函数并直接插入HashSet。
- 总结,HashSet先调用对象的hashcode函数来进行散列,当散列到不同位置时,则认为对象不相同且进行插入操作(不用判断equals函数);当散列到同一个位置才会调用对象的equals函数来进行比较,只当equals返回false则认为两个对象不相等才会进行插入操作,否则认为两个对象相同而不进行插入操作。
- 自定义对象,重载equals和hashcode时,应该保证两个对象的hashcode的比较结果 和 两个对象的equals函数比较结果,一样。
记住:
1. 如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。
2. 一般情况下,如果自定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals(),hashCode()方法,建立对象判断是否相同的依据。
如果HashSet不覆盖equals()和hashCode()方法的话,默认的是使用Object中的equals()和hashCode()方法。Object中的equals方法比较地址的,而不是比较内容,如果添加重复的对象,地址不同,就会认为没有重复的对象,所以比较对象是否相同,必须覆盖equals和hashCode方法
示例:
// 往HashSet集合中存储Person对象,如果姓名和年龄相同,视为同一个人,视为相同元素。
@Override
public boolean equals(Object obj) { // 判断Person的内容是否相同
if(this == obj) //this只是否为Person自身对象
return true;
if(!(obj instanceof Person)){
throw new ClassCastException("类型转化错误!");
}
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age; //字符创name的内容相同吗,年龄相同吗
}
@Override
public int hashCode() { // 判断Person的hash值是否相同
return name.hashCode()+ age;
}
HashSet hs = new HashSet();
hs.add(new Person("a",24));
hs.add(new Person("b",27));
Iterator it = hs.iterator();
while(it.hasNext()){
Person p = (Person)it.next();
System.out.println(p.getName()+"...."+p.getAge());
}
TreeSet
使用TreeSet自然排序,如果存放的是自定义对象,必须实现Comparable接口,覆盖里面的compareTo方法,添加元素的时候会进行比较;如果添加的是字符串就不需要覆盖compareTo方法,因为字符串有自己的compareTo方法
public class Person implements Comparable{...}
// 以Person对象年龄进行排序,如果年龄相同,就按照姓名排序
@Override
public int compareTo(Object o) {
if(!(o instanceof Person))
throw new ClassCastException("类型转化错误");
Person p = (Person)o;
int temp = this.age-p.age;
return temp==0 ? (this.name.compareTo(p.name)) : temp; // temp等于0表示年龄相等。
}
TreeSet ts = new TreeSet();
ts.add(new Person("zhangsan",28));
ts.add(new Person("lisi",21));
ts.add(new Person("zhouqi",29));
ts.add(new Person("zhaoliu",25));
ts.add(new Person("wangu",24));
ts.add(new Person("zhouqi",29));
Iterator it = ts.iterator();
while(it.hasNext()){
Person p = (Person)it.next();
System.out.println(p.getName()+":"+p.getAge());
}
public class Test {
public static void main(String[] argv) {
TreeSet<String> ts = new TreeSet();
ts.add("abc");
ts.add("zaa");
ts.add("aa");
ts.add("nba");
ts.add("abc");
Iterator it = ts.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
// 输出:
/*
aa
abc
nba
zaa
*/
使用TreeSet(Comparator< ? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
/**
* 创建了一个根据Person类的name进行排序的比较器。
* 先按照姓名进行比较,如果姓名相同,再按照年龄进行比较
*/
public class ComparatorByName implements Comparator {
@Override
public int compare(Object o1, Object o2) {
Person p1 = (Person)o1;
Person p2 = (Person)o2;
int temp = p1.getName().compareTo(p2.getName());
// 姓名相同,再比较年龄
return temp==0 ? p1.getAge()-p2.getAge(): temp;
}
}
TreeSet<Person> ts = new TreeSet(new ComparatorByName());
ts.add(new Person("zhangsan",28));
ts.add(new Person("lisi",21));
ts.add(new Person("zhouqi",29));
ts.add(new Person("zhaoliu",25));
ts.add(new Person("wangu",24));
Iterator it = ts.iterator();
while(it.hasNext()){
Person p = (Person)it.next();
System.out.println(p.getName()+":"+p.getAge());
}
/*
* 对字符串进行长度排序,再比较内容
*/
public class TreeSetTest {
public static void main(String[] args) {
// 接口内部类
TreeSet ts = new TreeSet(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int temp = o1.length() - o2.length();
return temp==0 ? o1.compareTo(o2) : temp;
}
});
ts.add("aaaaa");
ts.add("zz");
ts.add("nbaq");
ts.add("cba");
ts.add("abc");
ts.add("abc");
Iterator it = ts.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
// 输出:
/*
zz
abc
cba
nbaq
aaaaa
*/