SET、MAP底层原理

SET、MAP底层原理
1. SET
集合底层实现是否有序是否可重复是否可修改查询效率增删效率
std::set红黑树有序O(logn)O(logn)
std::multiset红黑树有序O(logn)O(logn)
std::unorded_set哈希表无序O(1)O(1)
2. MAP
映射底层实现是否有序(key)是否重复(key)是否修改(key)查询效率增删效率
std::map红黑树O(logn)O(logn)
std::multimap红黑树O(logn)O(logn)
std::unordered_map哈希表O(1)O(1)
3. 总结:

在STL中,虽然不同的底层实现的数据结构相同,但是分清不同的底层实现,了解其异同,根据不同的应用场景选择不同的底层实现,对提高程序的性能,减少不必要的计算大有用处。

<think>在Java中,SetMap是两个不同的接口,但Set的一些实现类(如HashSet,TreeSet,LinkedHashSet)确实是基于Map的实现类(如HashMap,TreeMap,LinkedHashMap)来实现的。这种设计利用了Map的键唯一性特性来保证Set中元素的唯一性。###理解"Set底层存储原理Map"1.**核心思想**:Set利用Map的键(Key)来存储元素,而Map的值(Value)则使用一个固定的虚拟对象(如`PRESENT`)填充。这样,Set只需要关注键的存在与否,而无需关心值的内容。2.**具体实现**:-**HashSet**:基于`HashMap`实现-**TreeSet**:基于`TreeMap`实现-**LinkedHashSet**:基于`LinkedHashMap`实现###代码示例:HashSet的源码分析在Java的`HashSet`类中,我们可以看到以下关键代码:```javapublicclassHashSet<E>extendsAbstractSet<E>implementsSet<E>,Cloneable,java.io.Serializable{privatetransientHashMap<E,Object>map;//虚拟值用于填充Map的值privatestaticfinalObjectPRESENT=newObject();publicHashSet(){map=newHashMap<>();}publicbooleanadd(Ee){returnmap.put(e,PRESENT)==null;}publicbooleanremove(Objecto){returnmap.remove(o)==PRESENT;}//其他方法...}```###解释:-**add方法**:当向Set中添加元素时,实际上是将该元素作为键放入底层Map中,而值则使用一个固定的`PRESENT`对象(静态常量)。-**remove方法**:移除元素时,实际上是移除底层Map中对应的键。-**contains方法**:检查元素是否存在,实际上是检查底层Map中是否包含该键。###为什么这样设计?1.**代码复用**:避免了重复实现哈希表、红黑树等复杂数据结构。2.**保证唯一性**:直接利用Map键的唯一性特性。3.**维护一致性**:SetMap的行为保持一致(如哈希冲突处理、树化逻辑等)。###TreeSet的类似实现```javapublicclassTreeSet<E>extendsAbstractSet<E>implementsNavigableSet<E>,Cloneable,java.io.Serializable{privatetransientNavigableMap<E,Object>m;privatestaticfinalObjectPRESENT=newObject();publicTreeSet(){this(newTreeMap<E,Object>());}//构造方法接受一个NavigableMapTreeSet(NavigableMap<E,Object>m){this.m=m;}publicbooleanadd(Ee){returnm.put(e,PRESENT)==null;}}```###优点:1.**开发效率**:减少重复代码。2.**维护性**:Map的优化会自动反映到Set上(如HashMap的性能优化也会提升HashSet的性能)。###注意事项:-**内存占用**:虽然值使用同一个虚拟对象,但每个键值对仍然占用一个Entry(或Node)的空间,因此HashSet的内存占用与HashMap存储相同数量键时类似。-**序列化**:Set实现类有自定义的序列化机制,不会直接序列化底层Map。###自定义Set实现示例如果我们想基于自己的Map实现一个Set,可以这样做:```javapublicclassCustomSet<E>implementsSet<E>{privatefinalMap<E,Boolean>map;//使用Boolean代替Object更节省内存publicCustomSet(){map=newCustomMap<>();//假设CustomMap是自定义的Map实现}@Overridepublicbooleanadd(Ee){returnmap.put(e,Boolean.TRUE)==null;}//其他Set方法的实现...}```###总结"Set底层存储原理Map"这一说法准确描述了Java中主要Set实现类的内部机制。通过将Set元素作为Map的键,并用一个虚拟常量作为值,实现了Set接口要求的唯一性集合操作。§§相关问题§§:1.为什么Java选择用Map来实现Set而不是独立实现?2.HashSetHashMap在哈希冲突处理上有什么共同点?3.如何基于ConcurrentHashMap实现一个线程安全的Set?4.在哪些场景下使用TreeSet比HashSet更合适?</think>### Set底层存储原理Map的关系详解 在Java集合框架中,SetMap有着紧密的联系,许多Set实现实际上是基于Map实现的。这种设计利用了Map键的唯一性特性来实现Set元素不重复的特性。 #### 核心原理Set作为Map的键视图 大多数Set实现类的本质是:**使用Map来存储数据,其中Set元素作为Map的键(Key),而Map的值(Value)使用一个固定的虚拟对象填充**。 这种设计的优点: 1. **代码复用**:重用Map的哈希表、红黑树等数据结构 2. **保证唯一性**:直接利用Map键的唯一性特性 3. **高效实现**:复用Map的查找、插入、删除算法 #### 具体实现分析 ##### 1. HashSet 基于 HashMap 实现 ```java // HashSet源码节选 public class HashSet<E> extends AbstractSet<E> implements Set<E> { private transient HashMap<E, Object> map; // 虚拟值常量 private static final Object PRESENT = new Object(); public HashSet() { map = new HashMap<>(); } public boolean add(E e) { return map.put(e, PRESENT) == null; } public boolean remove(Object o) { return map.remove(o) == PRESENT; } } ``` **原理说明**: - 所有元素存储在HashMap的键中 - 值使用固定的`PRESENT`对象(节省内存) - `add()`方法实际调用`map.put()` - `remove()`方法实际调用`map.remove()` ##### 2. TreeSet 基于 TreeMap 实现 ```java // TreeSet源码节选 public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E> { private transient NavigableMap<E, Object> m; private static final Object PRESENT = new Object(); public TreeSet() { this(new TreeMap<E, Object>()); } public boolean add(E e) { return m.put(e, PRESENT) == null; } } ``` **原理说明**: - 使用TreeMap作为底层存储 - 元素作为TreeMap的键 - 天然保持元素有序(根据自然顺序或Comparator) ##### 3. ConcurrentSkipListSet 基于 ConcurrentSkipListMap ```java // ConcurrentSkipListSet源码节选 public class ConcurrentSkipListSet<E> extends AbstractSet<E> { private final ConcurrentNavigableMap<E, Object> m; public ConcurrentSkipListSet() { m = new ConcurrentSkipListMap<E, Object>(); } public boolean add(E e) { return m.putIfAbsent(e, Boolean.TRUE) == null; } } ``` **原理说明**: - 使用跳表实现的并发Map - 提供线程安全的Set实现 - `putIfAbsent()`保证原子性添加 #### 自定义Set实现示例 理解原理后,我们可以基于HashMap实现自定义Set: ```java import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Set; public class CustomSet<E> implements Set<E> { private final HashMap<E, Object> map; private static final Object PRESENT = new Object(); public CustomSet() { this.map = new HashMap<>(); } @Override public int size() { return map.size(); } @Override public boolean isEmpty() { return map.isEmpty(); } @Override public boolean contains(Object o) { return map.containsKey(o); } @Override public boolean add(E e) { return map.put(e, PRESENT) == null; } @Override public boolean remove(Object o) { return map.remove(o) == PRESENT; } @Override public void clear() { map.clear(); } // 其他方法实现类似,都是委托给map... } ``` #### 内存结构图示 ``` HashSet 实例 ┌──────────────────────┐ │ HashSet │ │ ┌──────────────────┐ │ │ │ HashMap map │ │ │ │ ┌──────────────┐ │ │ │ │ │ Entry[] table│ │ │ │ │ │ ┌─────────┐ │ │ │ │ │ │ │ Entry1 │ │ │ │ │ │ │ │ key: A ├──┼─┼─┼─> Set元素A │ │ │ │ value: ────────┼─> PRESENT (虚拟对象) │ │ │ └─────────┘ │ │ │ │ │ │ ┌─────────┐ │ │ │ │ │ │ │ Entry2 │ │ │ │ │ │ │ │ key: B ├──┼─┼─┼─> Set元素B │ │ │ │ value: ────────┼─> PRESENT (同上) │ │ │ └─────────┘ │ │ │ │ │ └──────────────┘ │ │ │ └──────────────────┘ │ └──────────────────────┘ ``` #### 设计优势与考量 1. **复用优势**: - 直接复用Map的哈希算法冲突解决 - 复用Map的扩容机制 - 复用Map的迭代器实现 2. **内存考量**: - 每个元素需要额外的Map.Entry对象 - 虚拟值对象是共享的(static final),不增加额外开销 3. **性能特点**: - HashSet的操作时间复杂度与HashMap相同 - TreeSet的操作时间复杂度与TreeMap相同
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值