Java Hashset与Treeset
我一直很喜欢树,那漂亮的Java Hashset与Treeset,java,hashset,treeset,Java,Hashset,Treeset,我一直很喜欢树,那漂亮的O(n*log(n))和它们的整洁。然而,我认识的每一位软件工程师都尖锐地问我为什么要使用TreeSet。从CS的背景来看,我认为你使用什么并不重要,我也不想乱弄散列函数和存储桶(在Java的情况下) 在哪种情况下,我应该在树集上使用哈希集?哈希集是O(1)来访问元素,所以它当然很重要。但维护集合中对象的顺序是不可能的 TreeSet如果维护顺序(以值而不是插入顺序)对您来说很重要,那么它很有用。但是,正如您所注意到的,您正在用较慢的时间来访问一个元素:O(logn)进行
O(n*log(n))
和它们的整洁。然而,我认识的每一位软件工程师都尖锐地问我为什么要使用TreeSet
。从CS的背景来看,我认为你使用什么并不重要,我也不想乱弄散列函数和存储桶(在Java
的情况下)
在哪种情况下,我应该在树集上使用哈希集
?哈希集
是O(1)来访问元素,所以它当然很重要。但维护集合中对象的顺序是不可能的
TreeSet
如果维护顺序(以值而不是插入顺序)对您来说很重要,那么它很有用。但是,正如您所注意到的,您正在用较慢的时间来访问一个元素:O(logn)进行基本操作
从:
此实现为基本操作(添加
、删除
和包含
)提供了保证的日志(n)时间成本
如果您没有插入足够的元素,导致频繁的重置(或冲突,如果您的哈希集无法调整大小),哈希集肯定会为您提供恒定时间访问的好处。但是在有大量增长或收缩的集合上,根据实现的不同,使用树集合实际上可能会获得更好的性能
如果我记性好的话,一棵功能性红黑树的摊销时间可以接近O(1)。冈崎的书会有一个比我更好的解释。(或参见)当然,HashSet实现要快得多——因为不需要排序,所以开销更小。有关Java中各种集合实现的详细分析,请参阅
这里的讨论还指出了一个有趣的“中间地带”方法来解决树与散列问题。Java提供了一个LinkedHashSet,它是一个哈希集,其中运行着一个“面向插入”的链表,也就是说,链表中的最后一个元素也是最近插入到哈希中的元素。这允许您避免无序散列的无序性,而不会增加树集的成本 大多数使用HashSet的原因是(平均)操作是O(1)而不是O(logn)。如果集合包含标准项,则不会像为您所做的那样“胡乱使用哈希函数”。如果集合包含自定义类,则必须实现hashCode
才能使用HashSet
(虽然有效的Java演示了如何使用),但如果使用TreeSet
,则必须使其具有可比性
或提供比较器。如果类没有特定的顺序,这可能是一个问题
我有时会将TreeSet
(或者实际上是TreeMap
)用于非常小的集合/地图(<10项),尽管我没有检查这样做是否有任何实际好处。对于大型集合,差异可能相当大
现在,如果您需要排序,则树集是合适的,尽管即使如此,如果更新频繁且不经常需要排序结果,有时将内容复制到列表或数组并对其进行排序会更快。消息编辑(完全重写)当顺序不重要时,也就是此时。两者都应该给出Log(n)-这将有助于查看其中一个是否比另一个快5%。HashSet可以在循环中给出O(1)测试,这应该可以揭示它是否是。树集是两个排序集合之一(另一个是
树形图)。它使用红黑树结构(但您知道),并保证
元素将按照自然顺序以升序排列。或者,
您可以使用构造函数构造树集,该构造函数允许您为集合指定
自己的订单规则(而不是依赖于定义的订单
通过使用可比较的或比较器(根据元素的类别)
而LinkedHashSet是一个有序的HashSet版本
跨所有元素维护双链接列表。使用这个类而不是HashSet
当您关心迭代顺序时。当您遍历哈希集时
顺序是不可预测的,而LinkedHashSet允许您遍历元素
按照插入顺序,HashSet比TreeSet快得多(对于大多数操作,如添加、删除和包含,固定时间与日志时间),但不提供TreeSet的排序保证。
- 该类为基本操作(添加、删除、包含和大小)提供恒定的时间性能李>
- 它不能保证元素的顺序随时间保持不变
- 迭代性能取决于哈希集的初始容量和负载因子。
- 接受默认的负载系数是非常安全的,但是您可能希望指定一个初始容量,该容量大约是您期望该集增长到的大小的两倍
- 保证基本操作(添加、删除和包含)的日志(n)时间成本
- 保证集合的元素将被排序(升序、自然或您通过其构造函数指定的元素)(实现)
- 不为迭代性能提供任何调优参数
- 提供了一些方便的方法来处理有序集,如,
last()
等
要点:
- 两者都保证元素的重复自由收集
- 通常,将元素添加到HashSet,然后将集合转换为树集以进行重复的自由排序遍历会更快
- 这些实现都不是同步的。也就是说,如果多个线程同时访问一个集合,并且至少有一个线程修改该集合,则必须在外部对其进行同步
- LinkedHashSet在某种意义上介于
HashSet
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));
}
}
TreeSet<String> ts = new TreeSet<String>();
ts.add(null); // throws NullPointerException
HashSet<String> hs = new HashSet<String>();
hs.add(null); // runs fine
╔══════════════╦═════════════════════╦═══════════════════╦═════════════════════╗
║ 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 ║
╚══════════════╩═══════════════════════════════════════════════════════════════╝
TreeSet<String> treeSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
HashSet<String> hashSet = new HashSet<>();
treeSet.add("a");
hashSet.add("A");
System.out.println(hashSet.equals(treeSet));
System.out.println(treeSet.equals(hashSet));