回答
Set 接口是通过元素之间的比较来保证元素唯一性的,不同的实现类比较的方式不一样:
HashSet
和LinkedHashSet
是基于哈希表实现的。他们是通过元素的hashCode()
与equals()
来保证的,只有当hashCode()
相同且equals()
方法返回true
时,两个对象才被视为相同的,这样来保证元素的唯一性。TreeSet
是基于红黑树实现的。它是通过元素实现的Comparable
接口或者在新建 TreeSet 时传入的Comparator
来保证元素的唯一性。
详解
在 Java 中 Set
接口是一个不包含重复元素的集合,它主要有两类实现:
HashSet
和LinkedHashSet
是基于哈希表实现的。TreeSet
是基于红黑树实现的。
他们是通过如下方式来保证元素的唯一性的。
HashSet
HashSet
使用哈希表来存储元素,底层是基于 HashMap 来实现的。当向 HashSet 中添加元素时,它首先计算出该元素的 hashcode 值,然后在通过扰动计算和按位与的方式计算出该元素的位置,若这个位置为空,则添加进去,若不为空,则通过 equals()
比较两者是否相同,如果相同,就不添加,不同,则找一个空位置添加。所以 HashSet
是通过 hashCode()
与 equals()
来保证的,只有当 hashCode()
相同且 equals()
方法返回 true
时,两个对象才被视为相同的,这样就保证了元素的唯一性。
LinkedHashSet
是 HashSet
的子类,只不过它是有序的,保证了迭代元素的顺序就是元素的插入顺序,元素的唯一值保证与 HashSet
一致。
关于 hashCode()
和 equals()
请阅读这篇文章:
为什么重写 equals() 就一定要重写 hashCode()
TreeSet
TreeSet
实现 SortedSet
接口,使用红黑树结构来存储元素。元素的唯一性有两种方式保证:
- 使用自然顺序,需要元素实现
Comparable
接口,并重写了compareTo()
。 - 在创建
TreeSet
的时候提供Comparator
来保证。
如果两个元素通过比较器被认为是相等的,那么只有一个元素会被存储在 TreeSet
中。
其实 Set 接口的元素唯一性是由元素的比较来实现的,只不过不同的实现,它的比较方式不同罢了。
扩展
HashSet 、LinkedHashSet、TreeSet 有什么区别?
一、内部结构不同
HashSet
基于哈希表实现,底层是通过HashMap
实现的。LinkedHashSet
也是基于哈希表实现的,但它使用了一个双向链表来维护元素的插入顺序,底层是通过LinkedHashMap
实现的。TreeSet
基于红黑树实现,底层是通过TreeMap
实现的。
二、功能不同
HashSet
只保证元素的唯一性。LinkedHashSet
保证元素的唯一性,同时也维持了元素的插入顺序。TreeSet
保证了元素的唯一性,同时还支持按照元素的自然顺序进行排序,或者根据创建TreeSet
时提供的Comparator
进行排序。
三、性能
HashSet
> LinkedHashSet
> TreeSet
。