使用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/