2023-08-25 09:00:07

树对树

我一直很喜欢树,O(n*log(n))和它们的整洁。然而,我所认识的每个软件工程师都尖锐地问过我为什么要使用TreeSet。从CS的背景来看,我不认为你使用什么很重要,我也不关心在哈希函数和桶(在Java的情况下)上搞得一团糟。

在哪些情况下,我应该在树集上使用HashSet ?


当前回答

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

LinkedHashSet是HashSet的有序版本 在所有元素之间维护一个双链接列表。使用这个类而不是HashSet 当你关心迭代顺序时。迭代HashSet时 顺序是不可预测的,而LinkedHashSet允许您迭代元素 按照它们被插入的顺序

其他回答

基于@shevchyk在地图上可爱的视觉回答,以下是我的看法:

╔══════════════╦═════════════════════╦═══════════════════╦═════════════════════╗
║   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               ║
╚══════════════╩═══════════════════════════════════════════════════════════════╝

HashSet是O(1)来访问元素,所以这当然很重要。但是保持集合中对象的顺序是不可能的。

如果维护顺序(根据值而不是插入顺序)对您很重要,TreeSet是有用的。但是,正如您所注意到的,您正在以顺序换取访问元素的更慢时间:基本操作为O(log n)。

来自TreeSet的javadocs:

该实现为基本操作(添加、删除和包含)提供了log(n)的时间成本。

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

LinkedHashSet是HashSet的有序版本 在所有元素之间维护一个双链接列表。使用这个类而不是HashSet 当你关心迭代顺序时。迭代HashSet时 顺序是不可预测的,而LinkedHashSet允许您迭代元素 按照它们被插入的顺序

如果您没有插入足够多的元素导致频繁重散列(或冲突,如果您的HashSet不能调整大小),那么HashSet当然可以为您提供常量时间访问的好处。但是对于有大量增长或收缩的集合,使用Treesets实际上可能会获得更好的性能,这取决于实现。

如果我没记错的话,平摊时间可以接近于一个功能性红黑树的O(1)。冈崎的书会有比我更好的解释。(或参阅他的出版物列表)

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

这里的讨论还指出了一种有趣的“中间地带”方法来解决树与哈希的问题。Java提供了一个LinkedHashSet,它是一个HashSet,其中运行着一个“面向插入”的链表,也就是说,链表中的最后一个元素也是最近插入到哈希中的元素。这允许您避免无序散列的无序性,而不会增加TreeSet的成本。