Set总结

本文详细解析了Java中Set接口及其实现类HashSet、LinkedHashSet、TreeSet和EnumSet的特点与内部实现原理,包括它们的数据结构、构造方法及关键方法。
Set接口

声明

public interface Set<E> extends Collection<E>

接口定义的方法:

int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean retainAll(Collection<?> c);
boolean removeAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
default Spliterator<E> spliterator();

除此之外还有两个跟排序有关的接口:
SortedSet:

//声明
public interface SortedSet<E> extends Set<E>

//新增方法
Comparator<? super E> comparator();
SortedSet<E> subSet(E fromElement, E toElement);
SortedSet<E> headSet(E toElement);
SortedSet<E> tailSet(E fromElement);
E first();
E last();
default Spliterator<E> spliterator();

NavigableSet:

//声明
public interface NavigableSet<E> extends SortedSet<E>

//新增方法
E lower(E e);
E floor(E e);
E ceiling(E e);
E higher(E e);
E pollFirst();
E pollLast();
Iterator<E> iterator();
NavigableSet<E> descendingSet();
Iterator<E> descendingIterator();
NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive);
NavigableSet<E> headSet(E toElement, boolean inclusive);
NavigableSet<E> tailSet(E fromElement, boolean inclusive);

可见实现了这两个接口的集合元素是有序的,并且要提供一系列排序相关的方法

Set类

抽象类AbstractSet继承了AbstractCollection
集合类HashSet继承了AbstractSet,实现了Set接口
集合类LinkedHashSet继承了HashSet,实现了Set接口
集合类TreeSet继承了AbstractSet,实现了NavigableSet接口
集合类EnumSet继承了AbstractSet

HashSet源码分析

声明:

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
private transient HashMap<E,Object> map;

此为HashSet的底层数据结构,可见HashSet是基于HashMap的,并且元素对应为HashMap中的key

private static final Object PRESENT = new Object();

HashMap中的value都使用这个默认Object
看构造方法:

public HashSet();
public HashSet(Collection<? extends E> c);
public HashSet(int initialCapacity, float loadFactor);
public HashSet(int initialCapacity);

//这个方法被子类LinkedHashSet调用,初始化一个LinkedHashMap
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

看其他方法:

public Iterator<E> iterator();
public int size();
public boolean isEmpty();
public boolean contains(Object o);
public boolean add(E e);
public boolean remove(Object o);
public void clear();
public Object clone();
public Spliterator<E> spliterator();

综上可知,HashSet底层基于HashMap实现,元素对应于HashMap中的key,元素不可重复,同HashMap一样是无序集合

LinkedHashSet源码分析

声明:

public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable

看构造方法:

public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
}

public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
}

public LinkedHashSet() {
        super(16, .75f, true);
}

public LinkedHashSet(Collection<? extends E> c) {
        super(Math.max(2*c.size(), 11), .75f, true);
        addAll(c);
}

这些构造方法都是调用HashSet中的构造方法初始化了一个LinkedHashMap
继承了HashSet的所有方法
综上可知,LinkedHashSet底层基于LinkedHashMap实现,元素不可重复,是有序集合,记录了元素的插入顺序,用迭代器遍历时是按元素插入顺序遍历的

TreeSet源码分析

声明:

public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable

实现了NavigableSet,所以是有序集合,要有一系列排序有关的方法

private transient NavigableMap<E,Object> m;

底层的数据结构
看构造方法:

public TreeSet() {this(new TreeMap<E,Object>());}
public TreeSet(Comparator<? super E> comparator) {this(new TreeMap<>(comparator));}
public TreeSet(Collection<? extends E> c);
public TreeSet(SortedSet<E> s);

可见TreeSet底层实际上是基于TreeMap实现的
看其他方法:

public Iterator<E> iterator();
public Iterator<E> descendingIterator();
public NavigableSet<E> descendingSet();
public int size();
public boolean isEmpty();
public boolean contains(Object o);
public boolean add(E e);
public boolean remove(Object o);
public void clear();
public boolean addAll(Collection<? extends E> c);
public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive);
public NavigableSet<E> headSet(E toElement, boolean inclusive);
public NavigableSet<E> tailSet(E fromElement, boolean inclusive);
public SortedSet<E> subSet(E fromElement, E toElement);
public SortedSet<E> headSet(E toElement);
public SortedSet<E> tailSet(E fromElement);
public Comparator<? super E> comparator();
public E first();
public E last();
public E lower(E e);
public E floor(E e);
public E ceiling(E e);
public E higher(E e);
public E pollFirst();
public E pollLast();
public Object clone();
public Spliterator<E> spliterator();

综上可知,TreeSet底层基于TreeMap实现,元素对应于TreeMap的key,故是有序集合,元素不可重复,根据指定比较器或自然序进行排序,并有一系列的排序相关的方法

EnumSet源码分析

声明:

public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> implements Cloneable, java.io.Serializable

没有构造方法,可以用静态方法返回一个实例:

public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType);
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType);
public static <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s);
public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c);
public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s);
public static <E extends Enum<E>> EnumSet<E> of(E e);
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2);
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3);
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4);
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5);
public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest);
public static <E extends Enum<E>> EnumSet<E> range(E from, E to);

其他方法:

public EnumSet<E> clone();
//除此之外还继承了AbstractSet的所有其他方法

综上可知,EnumSet必须存储Enum类型的值,故底层是基于位向量的,元素不能重复,各种操作的效率都很高

<think>好的,我现在需要总结关于集合Set)插入操作的相关知识,结合用户提供的引用和之前的对话内容。首先,用户之前的问题是关于C++中std::set的插入方法,现在想进一步总结不同数据结构中Set的插入操作,可能涉及其他语言如Java,以及不同数据结构的实现,比如哈希表、红黑树等。 根据用户提供的引用,引用[1]提到Redis的Set集合插入操作,引用[2]和[4]分别涉及C++和JavaSet实现,引用[3]提到了Java中Map和Set的关系。需要整合这些信息,同时参考之前的回答,其中已经讨论了C++的std::set插入方法。 首先,需要明确不同编程语言和数据结构中Set的实现差异。例如,C++的std::set基于红黑树,而Java的HashSet基于哈希表,TreeSet基于红黑树。Redis的Set则是另一种实现,可能使用哈希表或整数集合。 接下来,插入操作的共同点和差异。比如,所有Set实现都保证元素唯一性,但插入的时间复杂度不同:树实现的Set插入是O(log n),哈希表是平均O(1),最坏O(n)。此外,不同语言的API使用方式不同,比如C++的insert返回pair,而Java的add返回boolean。 还需要考虑批量插入的方式。C++允许通过迭代器范围插入,Java有addAll方法,Redis可能有SADD命令支持多个元素。此外,引用[2]提到集合运算,但用户当前关注的是插入操作,可能需要简要提到相关操作,但重点在插入。 用户之前的回答已经覆盖了C++的插入,现在需要扩展到其他语言和系统如Redis,同时保持结构清晰。需要分点讨论不同实现,比较它们的插入操作、时间复杂度、返回值等。同时,用户提供的引用中有关于Java的HashSet遍历和哈希值的部分,这可能与插入后的遍历有关,但插入本身更关注如何添加元素。 需要确保总结全面,涵盖主要的数据结构和语言,同时引用正确的文献。例如,引用[4]中的Java代码展示了HashSet的add方法,引用[3]提到entrySet等方法,但插入主要涉及add或put操作。需要检查每个引用中与插入相关的内容,合理组织到总结中。 最后,生成相关问题时要围绕Set插入的深入问题,如性能比较、选择不同实现的考虑因素、底层原理等。确保问题有针对性,帮助用户进一步学习。</think>### 关于集合Set)插入操作的综合性总结 集合Set)是一种核心数据结构,其核心特性为 **元素唯一性** 和 **自动去重**。不同编程语言及实现方式下的插入操作存在差异,以下是分类总结: --- #### 一、数据结构实现与插入特性 1. **基于红黑树的有序集合** - **C++ `std::set`** 使用红黑树实现,插入操作自动排序,时间复杂度为 $O(\log n)$。 **插入方法**: ```cpp auto result = mySet.insert(value); // 返回 pair<iterator, bool> if (result.second) { /* 插入成功 */ } ``` 支持范围插入: ```cpp s.insert(v.begin(), v.end()); // 从其他容器批量插入[^2] ``` - **Java `TreeSet`** 同样基于红黑树,插入后元素按自然顺序排序,时间复杂度 $O(\log n)$。 **插入方法**: ```java boolean isNew = treeSet.add(value); // 返回是否为新元素 ``` 2. **基于哈希表的无序集合** - **Java `HashSet`** 使用哈希表实现,插入时间复杂度为 $O(1)$(平均情况),最坏情况 $O(n)$。 **插入方法**: ```java HashSet<String> set = new HashSet<>(); set.add("hello"); // 返回是否为新元素[^4] ``` - **Redis Set** 使用哈希表或整数集合(小规模数据时)实现,插入命令为 `SADD`,支持批量操作: ```redis SADD myset "value1" "value2" // 返回新增元素数量[^1] ``` --- #### 二、插入操作的核心共性 1. **唯一性保证** 所有集合实现均自动拒绝重复元素,插入操作会隐式检查元素是否存在。 - *C++*: 通过 `insert()` 的返回值 `pair.second` 判断是否插入成功[^2]。 - *Java*: 通过 `add()` 的布尔返回值判断[^4]。 - *Redis*: `SADD` 返回新增元素数量,若为0则表示元素已存在[^1]。 2. **批量插入支持** - C++ 使用迭代器范围插入[^2]。 - Java 使用 `addAll(Collection)`。 - Redis 通过 `SADD key value1 value2 ...` 一次性插入多个值。 --- #### 三、插入操作的性能对比 | 实现方式 | 时间复杂度 | 排序特性 | 适用场景 | |----------------|------------|----------|------------------------| | 红黑树(Tree) | $O(\log n)$ | 有序 | 需要有序遍历或范围查询 | | 哈希表(Hash) | $O(1)$ | 无序 | 快速查找、去重操作 | | Redis Set | $O(1)$~$O(n)$ | 无序 | 分布式缓存、集合运算 | --- #### 四、插入相关的扩展操作 1. **集合运算与插入** - *C++* 可通过 `set_union`、`set_intersection` 生成新集合后插入[^2]。 - *Redis* 支持 `SINTERSTORE`(存储交集)、`SUNIONSTORE`(存储并集)直接生成结果集。 2. **元素移动** Redis 提供 `SMOVE` 命令将元素从一个集合移动到另一个集合。 --- ### 总结 集合的插入操作是数据管理的基础,**选择具体实现时需权衡排序需求、性能及语言特性**。例如,需要高频插入且无需排序时,哈希表实现更优;若需有序遍历,则红黑树或 `TreeSet` 更合适。 --- ### 相关问题 1. 不同数据结构(红黑树 vs 哈希表)的集合在插入性能上如何权衡? 2. Redis Set 的 `SADD` 命令在分布式环境下如何保证原子性? 3. 如何通过 Java 的 `HashSet` 实现高效批量插入并去重? 4. C++ 中 `std::set` 的 `insert` 方法返回值如何用于优化插入逻辑? 5. 集合插入操作的时间复杂度是否受元素类型(如自定义对象)影响?如何优化? [^1]: Redis 集合操作文档 [^2]: C++ STL 集合算法 [^3]: Java 集合框架 : Java HashSet 示例代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值