使用Set集合的主要原因是因为Set集合里面没有重复的元素。Set集合有三个常见的实现类:HashSet,TreeSet,LinkedHashSet。什么时候,选择哪一个使用非常重要。简单的说,如果你关注性能,应该使用HashSet;如果你需要一个有序的Set集合,应该使用TreeSet;如果你需要一个Set集合保存了原始的元素插入顺序,应该使用LinkedHashSet。
Set接口
Set接口继承Collection接口。Set集合不允许里面存在重复元素,每个元素都必须是唯一的。你只需要往Set集合简单的添加元素,重复元素会被自动移除。
HashSet,TreeSet,LinkedHashSet对比
HashSet是基于散列表实现的,元素没有顺序;add、remove、contains方法的时间复杂度为O(1)。
TreeSet是基于树实现的(红黑树),元素是有序的;add、remove、contains方法的时间复杂度为O(log (n))。因为元素是有序的,它提供了若干个相关方法如first(), last(), headSet(), tailSet()等;
LinkedHashSet介于HashSet和TreeSet之间,是基于哈希表和链表实现的,支持元素的插入顺序;基本方法的时间复杂度为O(1);
TreeSet例子
TreeSet<Integer> tree = new TreeSet<Integer>();
tree.add(12);
tree.add(63);
tree.add(34);
tree.add(45);
Iterator<Integer> iterator = tree.iterator();
System.out.print("Tree set data: ");
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
结果输出:
Tree set data: 12 34 45 63
现在,我们换个元素类型,在进行插入,首先定义一个Dog类,如下
class Dog {
int size; public Dog(int s) {
size = s;
} public String toString() {
return size + "";
}
}
然后,往TreeSet添加若干个Dog对象,如下:
public class Q17 { public static void main(String[] args) {
TreeSet<Dog> dset = new TreeSet<Dog>();
dset.add(new Dog(2));
dset.add(new Dog(1));
dset.add(new Dog(3));
Iterator<Dog> iterator = dset.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
} }
以上代码,编译OK,但是运行时报错,如下:
Exception in thread "main" java.lang.ClassCastException: simplejava.Dog cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1188)
at java.util.TreeMap.put(TreeMap.java:531)
at java.util.TreeSet.add(TreeSet.java:255)
at simplejava.Q17.main(Q17.java:22)
为什么呢?因为TreeSet是有序的,Dog类需要实现java.lang.Comparable接口的compareTo(),如下:
class Dog implements Comparable<Dog>{
int size; public Dog(int s) {
size = s;
} public String toString() {
return size + "";
} @Override
public int compareTo(Dog o) {
return size - o.size;
}
}
结果输出:
1 2 3
HashSet例子
HashSet<Dog> dset = new HashSet<Dog>();
dset.add(new Dog(2));
dset.add(new Dog(1));
dset.add(new Dog(3));
dset.add(new Dog(5));
dset.add(new Dog(4));
Iterator<Dog> iterator = dset.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
结果输出:
5 3 2 1 4
注意顺序是不确定的。
LinkedHashSet例子
LinkedHashSet<Dog> dset = new LinkedHashSet<Dog>();
dset.add(new Dog(2));
dset.add(new Dog(1));
dset.add(new Dog(3));
dset.add(new Dog(5));
dset.add(new Dog(4));
Iterator<Dog> iterator = dset.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
结果输出如下,保存了插入顺序:
2 1 3 5 4
性能测试
以下代码测试了这三个类add方法的性能:
Random r = new Random();
HashSet<Dog> hashSet = new HashSet<Dog>();
TreeSet<Dog> treeSet = new TreeSet<Dog>();
LinkedHashSet<Dog> linkedSet = new LinkedHashSet<Dog>();
// start time
long startTime = System.nanoTime();
for (int i = 0; i < 1000; i++) {
int x = r.nextInt(1000 - 10) + 10;
hashSet.add(new Dog(x));
}
// end time long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println("HashSet: " + duration);
// start time
startTime = System.nanoTime();
for (int i = 0; i < 1000; i++) {
int x = r.nextInt(1000 - 10) + 10;
treeSet.add(new Dog(x));
}
// end time
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("TreeSet: " + duration);
// start time
startTime = System.nanoTime();
for (int i = 0; i < 1000; i++) {
int x = r.nextInt(1000 - 10) + 10;
linkedSet.add(new Dog(x));
}
// end time
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("LinkedHashSet: " + duration);
结果如下,我们可以发现,HashSet性能最好(注:以上代码我自己本地测试,HashSet不一定比LinkedHashSet快...)。
HashSet: 2244768
TreeSet: 3549314
LinkedHashSet: 2263320
这个测试并不是很精准,但是基本可以反映出TreeSet是性能最差的,因为需要排序。
相关阅读:ArrayList vs. LinkedList vs. Vector
译文链接:http://www.programcreek.com/2013/03/hashset-vs-treeset-vs-linkedhashset/