Set接口是Java中用于存储无序且不重复元素的集合。它继承了Collection接口,但没有对其进行扩展。Set中的元素的无序性并不意味着它们是随机排列的,而是意味着它们在底层存储位置上没有特定的顺序。Set接口的主要实现类包括HashSet、TreeSet和LinkedHashSet。
HashSet是基于哈希表实现的,具有快速插入、删除和查找操作的特点,适用于需要高效查找的场景。TreeSet是基于红黑树实现的,可以对元素进行排序,并提供了一系列与排序相关的方法,适用于需要排序功能的场景。LinkedHashSet是基于哈希表和链表实现的,保持了元素的插入顺序,适用于需要保持插入顺序的场景。
HashSet是Set接口的典型实现,使用Set集合时通常使用这个实现类。HashSet按照Hash算法来存储集合中的元素,因此具有较好的存取和查找性能。然而,HashSet不能保证元素的排列顺序,并且不是线程安全的。此外,集合中的元素可以为null。Set集合与List集合的存取元素方式相同。
TreeSet是Set接口的另一个实现类,与HashSet一样,TreeSet可以确保容器内元素的唯一性。但是,它们的底层实现方式不同。TreeSet底层使用自平衡的排序二叉树来实现,因此既能保证元素的唯一性,又能对元素进行排序。TreeSet还提供了一些特有的方法。
HashSet能够保证元素的唯一性是因为它的底层是哈希表结构。当一个元素要存入HashSet集合时,首先通过调用自身的hashCode()方法计算出一个值,然后根据该值查找元素在集合中的位置。如果该位置没有元素,则将该元素存入集合;如果该位置上有元素,则继续调用该元素的equals()方法进行比较。如果equals方法返回为真,说明这两个元素是相同的,因此不会存储;否则,会在同一位置上存储两个元素(通常不可能重复)。因此,当一个自定义对象想正确地存入HashSet集合时,应该重写自定义对象的hashCode()和equals()方法。
LinkedHashSet在HashSet的基础上添加了两个指针域before和after,用于链接各个元素节点,从而记录了元素的插入顺序。因此,LinkedHashSet实际上是链表和哈希表的组合结构。在迭代访问性能方面,LinkedHashSet表现出色。由于它维护了一个按插入顺序运行的链表,因此在遍历set的所有元素时,LinkedHashSet提供了高效且稳定的性能。这使得它在需要频繁迭代的场景下成为一个很好的选择。