可变的数组-----集合类之Set接口
在前面两篇我讲了List接口和他的两个实现类,在这一篇我和大家一起探讨一下Set接口和他的实现类。
我觉的在集合的所有接口里,只有Set接口与数学上的集合的概念是最接近的,因为Set结合包含了数学上集合的三大特性中的两个:
1.不包含重复元素。
2.元素之间没有顺序。
这也是Set和List的区别。这个接口也有两个实现类:HashSet和TreeSet。
HashSet类
这个类的特点是元素的顺序是不确定的,而且不能添加相同的元素。那他是怎么判断两个元素是否相同呢?他需要满足下面这个关系式:
e==null ? e2==null : e.equals(e2)
通过上面这个表达式我们可以看出,就算是NULL,HashSet中也只能存在一个。对于非空元素,只要集合中没有能和他equals()返回true的,就可以加入。
在这个类中,并没有重新equals()方法,所以他是继承的Object类中的方法,在这里我们来看一下这个方法的源码:
public boolean equals(Object obj) {
return (this == obj);
}
大家都知道两个对象是否“==”,看的是toString()方法,我们再看一下他的源码:
public String toString()
{
return getClass().getName()+"@"+Integer.toHexString(hashCode());
}
在这里大家就可以看出他先是比较了这两个类是否是同一个类,然后再比较对象地址的哈希码表示是否相同。
这里提到了方法hashCode():返回该对象的哈希码值。
他有一下这么几个规定:
1.在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
2.如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
3.如果根据 equals(java.lang.Object)方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
这段文字摘自帮助文档中,主要表达的意思是对于不同的对象会产生不同的哈希码值。
所以除非类中重写了equals()或hashCode()(String类重写了该方法),否则同类两个对象的比较是比较内存地址是否相同。
当使用 HashSet 时,hashCode()方法就会得到调用,判断已经存储在集合中的对象的hash code 值是否与增加的对象的 hashcode 值一致;如果不一致,直接加进去;如果一致,再进行 equals 方法的比较,equals 方法如果返回 true,表示对象已经加进去了,就不会再增加新的对象,否则加进去。
关于这个类的几个方法:
构造方法:publicHashSet();构造一个新的空set,其底层 HashMap 实例的默认初始容量 是16;
添加元素:public boolean add(E e):如果此 set 中尚未包含指定元素,则添加指定元素。
删除元素:public boolean remove(Object o):如果指定元素存在于此set 中,则将其移除。
迭代器:public Iterator<E> iterator():返回对此set 中元素进行迭代的迭代器。
我们来说一下最后一个方法,因为Set集合中的元素是无序的,所以我们不能像数组一样用索引一样来调用,也不能像链表一样用引用来调用。为了解决调用的问题,我们引入了迭代器的概念,并非只有HashSet类中有迭代器,每个集合中都有迭代器(包括List接口)。
迭代器有以下几个方法:
boolean hasNext():如果仍有元素可以迭代,则返回true。(换句话说,如果next 返回了元素而不是抛出异常,则返回 true)。也就是说,如果还有下一个元素就返回true。
E next():返回下一个元素。
我们写一个程序来说明一下:
import java.util.HashSet;
import java.util.Iterator;
public class HashSetTest {
public static void main(String[]args) {
HashSet setOne = new HashSet();
setOne.add("a");
for(Iterator iter =setOne.iterator();iter.hasNext();)
{
System.out.println(iter.next());
}
}
}
这里表现了迭代器最常见的用法,也称作遍历集合。下面我们来讲一下:TreeSet
TreeSet
TreeSet类不仅实现了Set接口,也实现了SortedSet接口,因此TreeSet类在遍历集合的时候可以按自然顺序排序,也可以按自定义顺序来排序(String类是按字典的顺序排序的)。
而如何来定义排序的顺序呢?
这就要说到另一个接口了:Comparator.
这个接口定义了比较器,一般在使用TreeSet时我们会写一个这个接口的实现类,来完成我们需要的功能,例如我们要为学生的成绩按从高到低排序,可以这样写:
import java.util.*;
public class TreeSetTest {
public static void main(String[]args) {
TreeSet setOne = new TreeSet(new ComparatorOne());
setOne.add(new Person(90));
setOne.add(new Person(100));
setOne.add(new Person(80));
for(Iterator ite =setOne.iterator();ite.hasNext();)
{
System.out.println(((Person)ite.next()).getSocer());
}
}
}
class Person
{
private int socer;
public int getSocer() {
return socer;
}
public void setSocer(int socer) {
this.socer = socer;
}
public Person(int socer)
{
this.socer = socer;
}
}
class ComparatorOne implements Comparator
{
public int compare(Object o1, Object o2) {
Person personOne = (Person)o1;
Person personTwo = (Person)o2;
returnpersonTwo.getSocer()-personOne.getSocer();
}
}
因为我们想要让成绩高的排在前面,所以我们返回
personTwo.getSocer()-personOne.getSocer();
如果第一个同学的成绩高于第二个就返回负数,而在TreeSet中返回负数的排在前面。
有关TreeSet的构造方法常用的有两个:
1.public TreeSet();
2.publicTreeSet(Comparator comparator)
我们在实际应用中一般用第二个,因为我们需要自己定义比较方法。
关于Set接口我就说这么多吧。