• 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/

  • 相关阅读:
    ASP 禁止页面被缓存
    关于学习编程的一点想法
    今天下午做的一个关于web前端性能/性能测试的Talk
    错误植入法与老祖宗的智慧
    关闭sitwithwhom.51.net
    让Pywinauto支持中文菜单
    勤学与好问
    LoadRunner 的 web_set_user 函数
    有奖调查——《软件性能测试过程详解与案例剖析》第二版内容调查
    论“响应时间”
  • 原文地址:https://www.cnblogs.com/chenpi/p/5497125.html
Copyright © 2020-2023  润新知