目录
一.HashSet的实现原理
HashSet 是基于 HashMap 实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为present,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层HashMap 的相关方法来完成,HashSet 不允许重复的值。
1.添加元素
HashSet 的add()方法用于向集合中添加元素。这个方法内部调用HashMap的put()方法,将元素作为键插入到 HashMap 中。
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
2.删除元素
HashSet 的remove()方法用于从集合中删除指定的元素。这个方法内部调用 HashMap的 remove()方法,将对应的键(即元素)从 HashMap中移除。
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
3.判断元素是否存在
HashSet 的contains()方法用于判断集合中是否包含指定的元素。这个方法内部调用 HashMap的containsKey()方法,判断 HashMap中是否存在该键(即元素)。
public boolean contains(Object o) {
return map.containsKey(o);
}
4.迭代器
返回对此 set 中元素进行迭代的迭代器。返回元素的顺序并不是特定的。
public Iterator<E> iterator() {
return map.keySet().iterator();
}
5.性能
HashSet 的添加、删除和查找操作的性能通常都是常数时间的(O(1)),这得益于 HashMap的性能。
6.并发性
HashSet不是线程安全的。如果你需要线程安全的集合,可以使用Collections.synchronizedSet(new HashSet<>(...))或者ConcurrentHashMap的键集。
7.序列化
HashSet 实现了 Serializable 接口,这意味着它可以被序列化和反序列化。
二.HashSet的线程安全性
HashSet 是非线程安全的。这意味着多个线程同时修改 HashSet可能会导致不可预知的行为。如果多个线程需要访问和修改同一个 HashSet实例,必须通过外部同步来确保线程安全。
如果你需要线程安全的Set集合,可以考虑以下替代方案:
(1)Collections.synchronizedSet:通过这个静态方法包装一个 HashSet,可以提供简单的线程安全。但是,这种方法的并发性能较低,因为每次访问都需要获取锁。
(2)ConcurrentHashMap 的 keySet 方法:ConcurrentHashMap 提供了 keySet 方法,它返回一个线程安全的 Set 视图。这个 Set 支持更高效的并发访问。
(3)CopyOnWriteArraySet:这是一个线程安全的 Set 实现,它在每次修改(添加或删除元素)时都会复制整个底层数组。这种方法适用于读多写少的场景。
// 使用 Collections.synchronizedSet 包装 HashSet
Set<String> syncSet = Collections.synchronizedSet(new HashSet<>());
syncSet.add("Element1");
syncSet.add("Element2");
// 使用 CopyOnWriteArraySet
Set<String> copyOnWriteSet = new CopyOnWriteArraySet<>();
copyOnWriteSet.add("Element1");
copyOnWriteSet.add("Element2");
// 迭代两个线程安全的 Set
System.out.println("Synchronized Set: " + syncSet);
System.out.println("CopyOnWriteArraySet: " + copyOnWriteSet);
三.HashSet的有序性
HashSet 不保证元素的顺序。它基于 HashMap实现,而 HashMap不保证键的顺序。因此,每次迭代 HashSet 时元素的顺序可能不同。
如果你需要一个有序的Set集合,你可以考虑以下几种方法:
(1)TreeSet: TreeSet 是一个有序的 Set集合,它基于红黑树实现。TreeSet可以确保元素处于排序状态,无论是按照自然顺序还是根据提供的 Comparator。
(2)LinkedHashSet: LinkedHashSet维护了元素的插入顺序,或者在构造时指定的最近期访问顺序。它内部使用 LinkedList 来维护元素的顺序,同时使用 HashMap 来保证元素的唯一性。
(3)使用 HashSet 与额外的数据结构: 如果你需要保留 HashSet 的所有特性,并且只需要偶尔的顺序访问,你可以在需要的时候将 HashSet 元素添加到一个 TreeSet或 LinkedHashSet 中,进行排序操作。
Set<Integer> hashSet = new HashSet<>();
hashSet.add(4);
hashSet.add(1);
hashSet.add(3);
hashSet.add(2);
// 仅在需要有序时进行排序
Set<Integer> sortedSet = new TreeSet<>(hashSet);
for (Integer number : sortedSet) {
System.out.println(number);
}
LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>(hashSet);
for (Integer number : linkedHashSet) {
System.out.println(number);
}