标签:set hashset treeset linkedhash
上篇博客讲了Collection接口的一些基本操作,这篇博客主要介绍Collection接口的子接口Set。
Set是一种无序的集合,其基本操作和Collection接口是差不多的,主要的不同点在于Set中不能重复元素而Collection集合是可以的。对于Set集合我们主要关心它的HashSet,TreeSet两个实现类。
一.HashSet
HashSet是Set接口的典型实现,大多数时候使用Set集合时就是使用这个类;HashSet通过hash算法确定元素的存储位置。值得注意的是HashSet中可以存储值为null的元素。
那么HashSet是怎样工作的呢?我们每传入一个元素,HashSet就调用该对象的HashCode()方法计算得到该对象的HashCode值,然后就根据这个值决定该元素在HashSet中的存储位置。如果两个对象通过equals()比较相等,但是计算出的HashCode值不相同,那么HashSet就将他们存储到不同的位置上去;如果两个对象的HashCode值相等,而通过equals()方法比较不相等,那么HashSet就将他们在同一个位置上以链表的形式存储。
综上我们可以知道HashSet判断两个对象相等的标准是:两个对象的HashCode值相同,并且两个对象通过equals()方法比较返回true。所以这两个方法对HashSet来说是十分重要的,我们需要保证当两个对象的HashCode值一样时,其通过equals()方法比较也会返回true。下面的程序中A,B,C类分别重写了自己的HashCode()和equals()方法。
public class A {
///重写A类的equals()方法,使其总是返回true
public boolean equals(Object obj){
return true;
}
}
public class B {
///重写B类的HashCode()方法,使其总是返回2
public int hashCode(){
return 2;
}
}
public class C {
///重写C类的equals()方法,使其总是返回true
public boolean equals(Object obj){
return true;
}
///重写C类的HashCode()方法,使其总是返回3
public int hashCode(){
return 3;
}
}
public class HashCodeTest {
public static void main(String[] args){
HashSet st = new HashSet();
//依次添加两个A,B,C类对象
st.add(new A());
st.add(new A());
st.add(new B());
st.add(new B());
st.add(new C());
st.add(new C());
//可以看到只输出了一个C类,而A,B类都输出了两个
System.out.println(st);
}
}
HashSet有一个叫LinkedHashSet的子类,LinkedHashSet的基本原理和HashSet是一样的,只是LinkedHashSet内部通过一个链表来维护插入元素的相对顺序,这样使得看起来元素是以插入的顺序保存的;当我们遍历LinkedHashSet时就可以将元素以其输入顺序输出了。
package lkl;
import java.util.LinkedHashSet;
public class LinkedHashSetTest {
public static void main(String[] args){
LinkedHashSet lt = new LinkedHashSet();
lt.add("java");
lt.add("c");
lt.add("c++");
lt.add("shell");
///从下面两次输出可以看出,元素总是以输入顺序输出
System.out.println(lt);
lt.remove("java");
lt.add("java"); ///从新添加后java被放到最后
System.out.println(lt);
}
}
二.TreeSet
TreeSet是Set的另一个重要的子类。与HashSet通过hash的方式确定元素的存储位置不同,TreeSet内部通过一棵红黑树来维护元素的相对次序;由于TreeSet中元素的有序性,相对HashSet,还提供了几个额外的方法,如下面的代码所示:
package lkl;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args){
TreeSet ts = new TreeSet();
ts.add(1);
ts.add(-2);
ts.add(7);
ts.add(4);
///从下面的输出可以看出TreeSet是有序的
System.out.println(ts);
///Comparator comparator()
///如果TreeSet采用了定制排序,则返回定制排序的Comparator
///如果采用了自然排序,则返回null
System.out.println(ts.comparator());
///Object first():返回集合中第一个元素
///Object last():返回集合中最后一个元素
System.out.println("集合中第一个元素为: "+ts.first());
System.out.println("集合中最后一个元素为: "+ts.last());
///Object lower(Object e):返回集合中第一个小于e的元素,e不一定在集合中
///Object higher(Object e):返回集合中第一个大于e的元素
///如果没有元素满足的话,返回null
System.out.println("集合中第一个小于5的元素是: "+ts.lower(5));
System.out.println("集合中第一个大于5的元素是: "+ts.higher(5));
///SortedSet subSet(Object e1,Object e2):返回Set从e1-e2之间的子集
System.out.println(ts.subSet(1,5));
///SortSet headSet(Object e):返回集合中小于e的元素组成的子集
System.out.println(ts.headSet(6));
///SortSet tailSet(Object e):返回集合中大于等于e元素组成的子集
System.out.println(ts.tailSet(1));
}
}
因为TreeSet是有序的,所以插入一个元素时总是要比较大小;这里进行大小比较时总是调用对象obj1.compareTo(Object obj2)方法(返回正整数表示obj1大于obj2,返回负整数时表示obj1小于obj2,返回0时表示相等),只有两个对象通过compareTo()比较返回0时,才认为两个对象相等。这导致一个问题,不同的对象不能被同时插入到TreeSet中,因为不同的对象不能通过comparaTo()方法进行比较;当我们将不同的对象插入到TreeSet中时会引发异常。
如果我们想自己定义排序的规则,我们可以借助Comparator接口。该接口中包含了一个int compare(T t1,T t2)的方法,该方法在t1>t2时返回正整数,在t1
package lkl;
import java.util.TreeSet;
import java.util.Comparator;
public class TreeSetComparatorTest {
///TreeSet原本是从从小到大输出的,现在自己定制排序后
///可以实现从大到小输出
public static void main(String[] args){
TreeSet ts = new TreeSet(new Comparator(){
public int compare(Object o1,Object o2){
int m1= (int)o1;
int m2= (int)o2;
if(m1==m2)
return 0;
if(m1>m2)
return -1;
return 1;
}
});
ts.add(1);
ts.add(2);
ts.add(-1);
System.out.println(ts);
}
}
最后要强调一句,如果我们向set中添加了可变的对象,然后又改变了该对象中的File,那么可能造成该对象在set中”不可见”的情况;如果我们向set中加入了可变的对象,那么我们不要在后面再改变其中File的值。
标签:set hashset treeset linkedhash
原文地址:http://blog.csdn.net/acm_lkl/article/details/44309059