相关链接:http://hi.baidu.com/gabe2008/blog/item/eb0448da41d14168d1164e6f.html
当我看到这个问题:Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?
比较标准的说法:Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。
而我的研究生同学居然说,判断是否重复是由系统内部实现的,他一时半会儿无法理解一个类还须重写equals来判断是否重复。我解释得已经蛮清楚,大体说法,与上一致,但他只接受代码层次的解释,那我对于以前写的TreeSetTest这个类进行简单的修改,才发现,我本不应该拿TreeSet这么特殊的类进行打比方,虽然它也继续于Set接口。
Set<Cat> set = new TreeSet<Cat>();
Cat a = new Cat("Qi");
Cat b = new Cat("Qi");
代码到这里已经编译报错了?因为,TreeSet是排序的集合,被排序的元素必须实现Comparable接口,那么实现呗。事实上,这里是运行时出错(ClassCastException,而不是编译期的错误。如果TreeSet的构造方法原型内是这个造型:Comparable<? super T>,那么会在编译检错,而Arrays.sort(E, Comparator<? super T> c),那会是如果没有实现Comparator的对象当做排序器,那么编译出错。原本JDK可以使用 class TreeSet<E extends Comparable>,这样的话就能在编译时候做检查。这样的话,就有了矛盾:那些没实现Comparable但是靠Comparator的机制就不行了。
class Cat implements Comparable 接口,那么按eclipse系统提示,一律返回1,我重写了Cat中的equals方法,用来说明如果Cat的name是一样的话,就返回true,按照我的设想,此TreeSet添加这个元素的时候一定不成功,可是遍历以后,发现它还是添加了两个"Qi",检索才知道,原来TreeSet是用Comparable的方法compareTo的返回值,完整代代码如下:
import java.util.*;
public class TreeSetTest {
public static void main(String[] args) {
Set<Cat> set = new TreeSet<Cat>();
set.add(new Cat("Cat1"));
set.add(new Cat("Cat2"));
set.add(new Cat("Cat"));
set.add(new Cat("Cat"));
set.add(new Cat("Cat")); //无法被成功添加,因为compareTo的结果是0,Cat name相同
Iterator<Cat> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next().name+":");
}
}
}
class Cat implements Comparable{
public String name;
public Cat(String name){
this.name = name;
}
public int compareTo(Object o) {
String name = ((Cat)o).name;
return this.name.compareTo(name);
}
}
这里我们走一个极端,把compareTo的方法返回值一律为return 0; 那么只会添加第一个元素。如以下代码所示:
import java.util.Iterator;
import java.util.TreeSet;
class Drink implements Comparable<Object>{
public String name;
public int compareTo(Object o){
return 0;
}
}
public class TreeSetDrink {
public static void main(String[] args) {
Drink one = new Drink();
Drink two = new Drink();
one.name = "Coffee";
two.name = "Tea"; //因为compareTo返回为0,添加不成功,当成重复元素。
TreeSet set = new TreeSet();
set.add(one);
set.add(two);
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(((Drink)iterator.next()).name); //输出只有Coffee
}
}
再就是想到Set另一个重要子类HashSet,这个集合类继承于Set接口,爷爷是Collection接口,与HashMap没有什么直系血缘关系。首先他接受一个参数的泛型,泛型的作用就是在编译期去严格检查输入类型,HashSet添加元素时,需要对这个泛型类的hashCode值进行判断,测试是否为重复元素,代码如下:
import java.util.HashSet;
import java.util.Set;
import java.util.Iterator;
public class HashSetTest {
public static void main(String[] args) {
Set<Pig> hashset = new HashSet<Pig>();
hashset.add(new Pig("X"));
hashset.add(new Pig("Y"));
hashset.add(new Pig("X"));
Iterator<Pig> iterator = hashset.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next().name);
}
}
}
class Pig{
public String name;
public Pig(String name){
this.name = name;
}
public boolean equals(Object o){ //如果不重写,那么所有的new Pig都会被加入,因为hashCode的默认是每个对象惟一的,与equals继承与Object,eqauls在那个阶段与==无异
String name = ((Pig)o).name;
if(this.name.equals(name)){
return true;
}else
return false;
}
public int hashCode(){ //重复测试,首先测试hashCode值,如果一样,再测试equals结果。
return 0; //这里只是测试方便这么写了,事实上,如果这么写,是没有任何存储效率的。
}
}