首页 文章

Hashset与Treeset

提问于
浏览
467

我一直很喜欢树木,它们很漂亮,而且它们整洁 . 但是,我所知道的每个软件工程师都有针对性地问我为什么要使用 TreeSet . 从CS背景来看,我并不关心哈希函数和桶(在 Java 的情况下) .

在哪种情况下,我应该使用 HashSet 而不是 TreeSet

13 回答

  • 10

    1.HashSet允许空对象 .

    2.TreeSet不允许null对象 . 如果您尝试添加null值,它将抛出NullPointerException .

    3.HashSet比TreeSet快得多 .

    例如

    TreeSet<String> ts = new TreeSet<String>();
     ts.add(null); // throws NullPointerException
    
     HashSet<String> hs = new HashSet<String>();
     hs.add(null); // runs fine
    
  • 20

    HashSet is much faster than TreeSet (constant-time versus log-time for most operations like add, remove and contains) but offers no ordering guarantees like TreeSet.

    HashSet

    • 该类为基本操作提供恒定的时间性能(添加,删除,包含和大小) .

    • 它不保证元素的顺序会随着时间的推移保持不变

    • 迭代性能取决于HashSet的初始容量和加载因子 .

    • 它's quite safe to accept default load factor but you may want to specify an initial capacity that'大约是您期望该组增长的两倍 .

    TreeSet

    • 保证基本操作的log(n)时间成本(添加,删除和包含)

    • 保证set的元素将被排序(升序,自然或您通过其构造函数指定的那个)(实现SortedSet

    • 不提供迭代性能的任何调整参数

    • 提供了一些方便的方法来处理有序集,如first()last()headSet()tailSet()

    重点:

    • 两者都保证元素的无重复收集

    • 将元素添加到HashSet然后将集合转换为TreeSet以进行无重复的排序遍历通常会更快 .

    • 这些实现都不同步 . 也就是说,如果多个线程同时访问一个集合,并且至少有一个线程修改了该集合,则必须在外部进行同步 .

    • LinkedHashSet 在某种意义上介于 HashSetTreeSet 之间 . 实现为具有贯穿它的链表的哈希表,但是, it provides insertion-ordered iteration which is not same as sorted traversal guaranteed by TreeSet .

    因此,使用选择完全取决于您的需求,但我觉得即使您需要有序集合,您仍然应该更喜欢HashSet来创建Set,然后将其转换为TreeSet .

    • 例如 SortedSet<String> s = new TreeSet<String>(hashSet);
  • 13

    尚未提及 TreeSet 的一个优点是它具有更大的"locality",这是说(1)如果两个条目在顺序附近,a2706580_在数据结构中彼此靠近,因此在内存中的简写; (2)这种放置利用了局部性原理,即相似频率的应用程序经常访问类似数据 .

    这与 HashSet 形成对比,后者将条目分布在整个内存中,无论它们的键是什么 .

    当从硬盘驱动器读取的延迟成本是从缓存或RAM读取的成本的数千倍,并且当数据确实是通过本地访问时, TreeSet 可能是更好的选择 .

  • 39

    HashSet 是O(1)来访问元素,所以它确实很重要 . 但是不可能保持集合中对象的顺序 .

    如果维护订单(在值而非插入订单方面)对您很重要,则 TreeSet 非常有用 . 但是,当你以更慢的时间访问元素时,交易顺序为:基本操作的O(log n) .

    来自javadocs for TreeSet

    此实现为基本操作(添加,删除和包含)提供有保证的log(n)时间成本 .

  • 20

    在@shevchyk的 Map 上基于可爱的visual answer这是我的看法:

    ╔══════════════╦═════════════════════╦═══════════════════╦═════════════════════╗
    ║   Property   ║       HashSet       ║      TreeSet      ║     LinkedHashSet   ║
    ╠══════════════╬═════════════════════╬═══════════════════╬═════════════════════╣
    ║              ║  no guarantee order ║ sorted according  ║                     ║
    ║   Order      ║ will remain constant║ to the natural    ║    insertion-order  ║
    ║              ║      over time      ║    ordering       ║                     ║
    ╠══════════════╬═════════════════════╬═══════════════════╬═════════════════════╣
    ║ Add/remove   ║        O(1)         ║     O(log(n))     ║        O(1)         ║
    ╠══════════════╬═════════════════════╬═══════════════════╬═════════════════════╣
    ║              ║                     ║   NavigableSet    ║                     ║
    ║  Interfaces  ║         Set         ║       Set         ║         Set         ║
    ║              ║                     ║    SortedSet      ║                     ║
    ╠══════════════╬═════════════════════╬═══════════════════╬═════════════════════╣
    ║              ║                     ║    not allowed    ║                     ║
    ║  Null values ║       allowed       ║ 1st element only  ║      allowed        ║
    ║              ║                     ║     in Java 7     ║                     ║
    ╠══════════════╬═════════════════════╩═══════════════════╩═════════════════════╣
    ║              ║   Fail-fast behavior of an iterator cannot be guaranteed      ║
    ║   Fail-fast  ║ impossible to make any hard guarantees in the presence of     ║
    ║   behavior   ║           unsynchronized concurrent modification              ║
    ╠══════════════╬═══════════════════════════════════════════════════════════════╣
    ║      Is      ║                                                               ║
    ║ synchronized ║              implementation is not synchronized               ║
    ╚══════════════╩═══════════════════════════════════════════════════════════════╝
    
  • 3

    大多数人使用 HashSet 的原因是操作(平均)是O(1)而不是O(log n) . 如果该套装包含标准物品,那么您将无法完成此项操作 . 如果集合包含自定义类,则必须实现 hashCode 以使用 HashSet (尽管Effective Java显示如何),但如果使用 TreeSet ,则必须使其为 Comparable 或提供 Comparator . 如果 class 没有特定的订单,这可能是一个问题 .

    我有时使用 TreeSet (或实际上 TreeMap )用于非常小的集合/ Map (<10项),尽管我还没有检查过这样做是否有任何实际的好处 . 对于大型套装,差异可能相当大 .

    现在,如果您需要排序,那么 TreeSet 是合适的,尽管如果更新频繁且需要排序结果很少,有时候复制内容到列表或数组并对它们进行排序可能会更快 .

  • -3

    如果您没有插入足够的元素来导致频繁的重新散列(或者碰撞,如果您的HashSet无法调整大小),HashSet肯定会为您提供持续时间访问的好处 . 但是在具有大量增长或缩减的集合上,使用Treesets实际上可能会获得更好的性能,具体取决于实现 .

    如果记忆为我服务,摊销时间可以接近O(1),功能红黑树 . Okasaki的书会有比我能说的更好的解释 . (或参见his publication list

  • 1

    当然,HashSet实现要快得多 - 开销较少,因为没有排序 . http://java.sun.com/docs/books/tutorial/collections/implementations/set.html提供了对Java中各种Set实现的良好分析 .

    那里的讨论还指出了树与哈希问题的一个有趣的“中间立场”方法 . Java提供了一个LinkedHashSet,它是一个HashSet,其中有一个“面向插入”的链表,也就是说,链表中的最后一个元素也是最近插入Hash的 . 这使您可以避免无序散列的不正常,而不会导致TreeSet的成本增加 .

  • 834

    TreeSet 是两个已排序集合之一(另一个是TreeMap) . 它使用红黑树结构(但你知道),并保证元素按照自然顺序按升序排列 . (可选)您可以构造一个带有构造函数的TreeSet,该构造函数允许您通过使用Comparable或Comparator为集合提供自己的规则(而不是依赖于元素类定义的顺序) .

    和_ LinkedHashSet 是HashSet的有序版本,它维护所有元素的双向链接列表 . 在关心迭代顺序时,请使用此类而不是HashSet . 当您遍历HashSet时,顺序是不可预测的,而LinkedHashSet允许您按插入顺序迭代元素

  • 3

    基于技术考虑,特别是围绕性能,已经给出了很多答案 . 据我说, TreeSetHashSet 之间的选择很重要 .

    但我宁愿说选择应首先由 conceptual 考虑因素驱动 .

    如果,对于您需要操作的对象,自然顺序没有意义,那么请不要使用 TreeSet .
    它是一个有序集,因为它实现了 SortedSet . 所以这意味着你需要覆盖函数 compareTo ,这应该与返回函数 equals 的内容一致 . 例如,如果你有一组名为Student的类的对象,那么我认为 TreeSet 没有意义,因为学生之间没有自然的顺序 . 您可以按平均等级订购它们,好吧,但这不是"natural ordering" . 函数 compareTo 不仅会在两个对象代表同一个学生时返回0,而且当两个不同的学生具有相同的成绩时也会返回0 . 对于第二种情况, equals 将返回false(除非您决定在两个不同的学生具有相同等级时使后者返回true,这将使 equals 函数具有误导性含义,而不是说错误含义 . )
    请注意 equalscompareTo 之间的一致性是可选的,但强烈建议 . 否则接口 Set 的 Contract 被破坏,使您的代码误导其他人,从而也可能导致意外行为 .

    这个link可能是关于这个问题的一个很好的信息来源 .

  • 4

    为什么在你可以吃橘子的时候有苹果?

    严肃的家伙和女孩 - 如果你的收藏很大,读到和写到很多次,你真的很重要 - 几毫秒在这里和那里人们都没有注意到 . 如果这真的很重要,那么为什么不喜欢使用你选择的任何系列,它解决了你的问题[即使它不是特别是任务的最佳收集类型]让你自己 . 该软件具有可塑性 . 必要时优化您的代码 . 鲍勃叔叔说过早优化是万恶之源 . Uncle Bob says so

  • 25

    消息编辑( complete rewrite )当订单无关紧要时,就是这样 . 两者都应该给出Log(n) - 看看它们是否比另一个快5%以上是有用的 . HashSet可以在循环中给出O(1)测试,以揭示它是否存在 .

  • 7
    import java.util.HashSet;
    import java.util.Set;
    import java.util.TreeSet;
    
    public class HashTreeSetCompare {
    
        //It is generally faster to add elements to the HashSet and then
        //convert the collection to a TreeSet for a duplicate-free sorted
        //Traversal.
    
        //really? 
        O(Hash + tree set) > O(tree set) ??
        Really???? Why?
    
    
    
        public static void main(String args[]) {
    
            int size = 80000;
            useHashThenTreeSet(size);
            useTreeSetOnly(size);
    
        }
    
        private static void useTreeSetOnly(int size) {
    
            System.out.println("useTreeSetOnly: ");
            long start = System.currentTimeMillis();
            Set<String> sortedSet = new TreeSet<String>();
    
            for (int i = 0; i < size; i++) {
                sortedSet.add(i + "");
            }
    
            //System.out.println(sortedSet);
            long end = System.currentTimeMillis();
    
            System.out.println("useTreeSetOnly: " + (end - start));
        }
    
        private static void useHashThenTreeSet(int size) {
    
            System.out.println("useHashThenTreeSet: ");
            long start = System.currentTimeMillis();
            Set<String> set = new HashSet<String>();
    
            for (int i = 0; i < size; i++) {
                set.add(i + "");
            }
    
            Set<String> sortedSet = new TreeSet<String>(set);
            //System.out.println(sortedSet);
            long end = System.currentTimeMillis();
    
            System.out.println("useHashThenTreeSet: " + (end - start));
        }
    }
    

相关问题