大家好,在上一篇文章中,我们已经了解了HashMap、TreeMap和LinkedHashMap之间的区别,在这篇文章中,我们将了解Java中HashSet、TreeSet和LinkedHashSet之间的区别,这是另一个针对1到2年有经验的开发人员的热门Java面试问题。LinkedHashSet、TreeSet和HashSet是Java集合框架中Set接口的三种最流行的实现。由于它们实现了Set 接口,所以它们遵循其不允许重复的契约,但在排序方面,它们之间有一个关键的区别。
例如,HashSet不保持任何顺序,而TreeSet则按照外部比较器指定的排序顺序保持元素,或者按照对象的自然顺序定义比较逻辑。同样的,LinkedHashSet将元素保持在它们被插入的顺序上。
所有这些实现,除了TreeSet 使用equals()方法来检查是否有重复,另一方面,TreeSet使用compareTo()或compare()方法来比较对象,如果equals方法与compareTo()或compare()方法不一致,就会破坏Set 接口唯一元素的契约。
在这个Java Collection教程中,我们将看到LinkedHashSet vs TreeSet vs HashSet 在不同方面的区别 ,例如速度、性能、排序、同步等等。基于这些差异,我们也可以决定何时在Java中使用LinkedHashSet vs TreeSet vs HashSet。
TL;DR, 在所有的一般用途中使用HashSet ,即你只需要存储唯一的元素而没有任何排序要求。如果你需要保持元素被添加到Set中的顺序,那么就使用LinkedHashSet,它提供的排序对性能影响很小。
当你绝对需要将元素按照特定的排序顺序保存时,你也可以使用TreeSet ,比如将员工按照年龄或工资的增加顺序保存。
记住,TreeSet 比LinkedHashSet 和HashSet 要慢得多,因为这个排序开销很大。BTW,如果你准备参加Java面试,我建议你看一下Markham写的《Java编程面试揭秘》。你会发现有很多这样的问题,并有非常详细的答案。
Java中LinkedHashSet vs TreeSet vs HashSet的区别
在比较它们之前,让我们看看它们之间的一些相似之处。它们都实现了Set和Collection接口,所以它们可以被传递到接受Set作为参数的方法中。虽然它们都是集合,但是它们的实现方式不同,比如LinkedHashSet是由linked list和HashMap支持的,TreeSet 在Java 5之前是作为TreeMap实现的,现在从Java 6开始使用NavigableMap,而HashSet在Java中也是由HashMap支持。
现在让我们来看看它们之间的比较。
1.同步化
所有这三种方法,即HashSet、TreeSet 和LinkedHashSet 都是不同步的。它们不能在多个线程之间共享,除非专门进行同步。不过,创建一个同步的Set很容易,你只需要使用java.util.Collections工具类,如下所示:
Java中同步HashSet的方法
Set s
在Java中同步LinkedHashSet
Set s
在Java中同步化TreeSet
Set s
2.2.排序
HashSet 不保证任何顺序,而TreeSet 通过使用compareTo()方法对所有对象进行自然排序,或者通过使用传递给它们的比较器 的compare()方法进行自定义排序。
另一方面, LinkedHashSet 也提供了排序支持,以保持元素按其被添加到集合中的顺序。
实际上,这个属性也是由它们各自的Map实现所支持的事实衍生出来的。关于这两个类的更多细节,请参见HashSet和HashMap的区别。
3.空元素
这个属性可以从HashMap、LinkedHashMap和TreeMap中推导出来,因为HashSet内部使用HashMap,LinkedHashSet内部使用LinkedHashMap,TreeSet内部使用TreeMap。
HashMap和LinkedHashMap都允许一个空键,这两个Set的实现也是如此。
另一方面,由于TreeMap 不允许空键,TreeSet也不允许空元素,当你试图添加一个空对象时,会抛出java.lang.NullPointerException。其主要原因是使用了compareTo()和compare()方法,如果有一个元素是空的,就会抛出NullPointerException,但这确实取决于实现。
4.4.实现
HashSet 内部使用一个带有假值对象的HashMap ,而LinkedHashSet 使用LinkedHashMap 来保证插入的顺序。当你迭代HashSet 的时候,顺序是不可预测的,但是当你迭代LinkedHashSet的时候,你会按照元素的添加顺序进行迭代。TreeSet是由一个可导航的Map支持的,如下图所示。
HashSet的源代码
LinkedHashSet的源代码
/**
* Constructs a new, empty linked hash set. (This package private constructor
* is only used
* LinkedHashMap
* factor
*/
TreeSet的源代码
public
早期的TreeSet是内部实现的TreeMap,但随着Java 1.6中NavigableMap的引入,其实现也发生了变化。
5.迭代器
三种Set的实现所返回的迭代器都是快速失败的,这意味着如果你在迭代开始后修改集合,即在没有使用迭代器的移除方法的情况下添加或删除元素,它将抛出ConcurrentModificationException。
另外,HashSet的迭代器并不保证任何顺序,而LinkedHashSet的迭代器可以让你按照元素的添加顺序进行迭代。你也可以看这 篇文章来了解更多关于Java中不同类型的Iterator。
6.性能
在HashSet、LinkedHashSet和TreeSet中,HashSet对于添加、搜索和删除等常见操作来说是最快的,LinkedHashSet紧随其后,因为当一个元素被插入或删除时,由于维护一个双链表的开销,它的性能会有一点下降。
TreeSet比这两个要慢得多,因为每次TreeSet有变化时,它都需要进行排序。这意味着在默认情况下,你应该使用HashSet,除非你真的需要LinkedHashSet 或者TreeSet。
你也可以查看这篇文章,了解Java中TreeSet和TreeMap之间的更多区别。
总结
这里有一个很好的表格来比较Set接口的不同实现,例如EnumSet、TreeSet、CopyOnWriteArraySet、HashSet、LinkedHashSet和ConcurrentSkipListSet的不同参数,例如数据结构、排序、迭代器以及它们如何处理空输入。
以上就是关于Java中LinkedHashSet vs TreeSet vs HashSet的所有区别。对于一般的Set需求,你应该使用HashSet ,你只需要存储唯一的元素,而不需要任何排序或顺序要求,如果你需要保持元素被添加到Set中的顺序,除了保证唯一的元素之外,使用LinkedHashSet。
如果你需要将你的元素保持在一个特定的排序顺序,可以使用TreeSet,你可以保持元素的自然顺序,或者你可以通过提供一个比较器来定制它们的排序顺序,例如,保持笔记的价值递增。
学习Java集合框架的相关Java教程
感谢你到目前为止阅读本文。如果你喜欢这个Java Set教程以及Java中HashSet、TreeSet和LinkedHashSet的区别,那么请与你的朋友和同事分享。如果您有任何问题或反馈,请留言。
**P.S. -**如果你是Java的新手,想掌握Java集合框架,那么你也可以查看这个列表的 最好的Java集合和流课程来深入学习Java集合框架。它包括Udemy和Pluralsight的最佳Java集合课程。