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

  • 相关阅读:
    关于ajaxfileupload的使用方法以及一些问题
    vs里根据json快速创建对应类的方法
    20175227张雪莹 2018-2019-2 《Java程序设计》第四周学习总结
    关于:socket阻塞、非阻塞,同步、异步、I/O模型
    推挽输出理解
    使用指针做形参来解决函数的副本机制
    c语言副本机制
    开关电源电容选择
    MFC入门示例之水平滚动条和垂直滚动条(CScroll Bar)
    MFC入门示例之组合框(CComboBox)、列表框(CListBox)
  • 原文地址:https://www.cnblogs.com/chenpi/p/5497125.html
Copyright © 2020-2023  润新知