HashSet vs TreeSet vs LinkedHashSet


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM