JDK 8 ConcurrentSkipListSet 源码详解(详细注释版)

JDK 8 ConcurrentSkipListSet 源码详解(详细注释版)

1. 类定义和基本属性

public class ConcurrentSkipListSet<E>
    extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable {
    
    // 序列化版本号
    private static final long serialVersionUID = -2479143111061671589L;

    /**
     * 底层使用ConcurrentSkipListMap实现
     * 利用Map的键来存储Set的元素
     * 值使用统一的虚拟对象PRESENT
     */
    private final ConcurrentNavigableMap<E,Object> m;

    /**
     * 用于映射键值的虚拟值对象
     * 所有元素都映射到这个相同的对象
     * 节省内存空间,避免为每个元素创建值对象
     */
    private static final Object PRESENT = new Object();

    /**
     * 默认构造方法
     * 创建一个空的ConcurrentSkipListSet
     * 使用元素的自然排序
     * 
     * 初始化过程:
     * 1. 创建ConcurrentSkipListMap实例
     * 2. 使用自然排序比较器
     * 3. 为空Set做好准备
     */
    public ConcurrentSkipListSet() {
        m = new ConcurrentSkipListMap<E,Object>();
    }

    /**
     * 使用指定比较器的构造方法
     * @param comparator 用于排序的比较器
     * 
     * 比较器说明:
     * - 如果comparator为null,使用自然排序
     * - 否则使用指定的比较器进行排序
     * - 比较器必须保持一致性
     * - 元素类型必须与比较器兼容
     */
    public ConcurrentSkipListSet(Comparator<? super E> comparator) {
        m = new ConcurrentSkipListMap<E,Object>(comparator);
    }

    /**
     * 从Collection构造ConcurrentSkipListSet
     * @param c 包含初始元素的集合
     * 
     * 构造过程:
     * 1. 使用自然排序创建底层Map
     * 2. 将集合中的所有元素添加到Set中
     * 3. 保持元素的唯一性和有序性
     * 
     * 注意事项:
     * - 集合中的元素必须可比较
     * - 重复元素会被自动去除
     * - 元素按照排序规则排列
     */
    public ConcurrentSkipListSet(Collection<? extends E> c) {
        m = new ConcurrentSkipListMap<E,Object>();
        addAll(c);
    }

    /**
     * 从SortedSet构造ConcurrentSkipListSet
     * @param s 包含初始元素的有序集合
     * 
     * 构造过程:
     * 1. 使用s的比较器创建底层Map
     * 2. 将有序集合中的元素添加到Set中
     * 3. 保持原有的排序顺序
     * 4. 去除可能的重复元素
     * 
     * 优势:
     * - 保持原有的排序特性
     * - 避免重新排序的开销
     * - 提高构造效率
     */
    public ConcurrentSkipListSet(SortedSet<E> s) {
        m = new ConcurrentSkipListMap<E,Object>(s.comparator());
        addAll(s);
    }

    /**
     * 私有构造方法(包私有)
     * 用于内部创建,直接指定底层Map
     * @param m 底层的ConcurrentNavigableMap
     * 
     * 设计目的:
     * - 支持子集视图的创建
     * - 避免重复创建Map实例
     * - 保持与原Set的关联关系
     */
    ConcurrentSkipListSet(ConcurrentNavigableMap<E,Object> m) {
        this.m = m;
    }

2. 核心集合操作方法(详细注释)

    /**
     * 添加元素到Set中
     * @param e 要添加的元素
     * @return 如果元素不存在则添加成功返回true,否则返回false
     * 
     * 添加过程:
     * 1. 调用底层Map的putIfAbsent方法
     * 2. 如果元素不存在,添加成功返回true
     * 3. 如果元素已存在,添加失败返回false
     * 4. 保持Set的唯一性约束
     * 
     * 时间复杂度:平均O(log n)
     * 线程安全性:完全线程安全
     */
    public boolean add(E e) {
        return m.putIfAbsent(e, PRESENT) == null;
    }

    /**
     * 从Set中移除指定元素
     * @param o 要移除的元素
     * @return 如果元素存在并成功移除返回true,否则返回false
     * 
     * 移除过程:
     * 1. 调用底层Map的remove方法
     * 2. 如果元素存在,移除成功返回true
     * 3. 如果元素不存在,移除失败返回false
     * 4. 保持Set的一致性
     * 
     * 时间复杂度:平均O(log n)
     * 线程安全性:完全线程安全
     */
    public boolean remove(Object o) {
        return m.remove(o) == PRESENT;
    }

    /**
     * 判断Set是否包含指定元素
     * @param o 要检查的元素
     * @return 如果包含返回true,否则返回false
     * 
     * 查找过程:
     * 1. 调用底层Map的containsKey方法
     * 2. 利用跳表的快速查找特性
     * 3. 保持O(log n)的查找性能
     * 
     * 时间复杂度:平均O(log n)
     * 线程安全性:完全线程安全
     */
    public boolean contains(Object o) {
        return m.containsKey(o);
    }

    /**
     * 返回Set中元素的个数
     * @return 元素个数
     * 
     * 计数特点:
     * 1. 调用底层Map的size方法
     * 2. 在高并发环境下可能返回估算值
     * 3. 保证不会抛出异常
     * 4. 性能较高,无需遍历
     * 
     * 时间复杂度:O(1)
     * 注意:在并发环境下可能不是精确值
     */
    public int size() {
        return m.size();
    }

    /**
     * 判断Set是否为空
     * @return 如果为空返回true,否则返回false
     * 
     * 判断过程:
     * 1. 调用底层Map的isEmpty方法
     * 2. 直接检查Map的大小
     * 3. 性能优异,时间复杂度O(1)
     * 
     * 时间复杂度:O(1)
     * 线程安全性:完全线程安全
     */
    public boolean isEmpty() {
        return m.isEmpty();
    }

    /**
     * 清空Set中的所有元素
     * 
     * 清空过程:
     * 1. 调用底层Map的clear方法
     * 2. 原子性地移除所有元素
     * 3. 释放相关资源
     * 4. 保持线程安全性
     * 
     * 时间复杂度:O(n)
     * 线程安全性:完全线程安全
     */
    public void clear() {
        m.clear();
    }

3. 迭代器相关方法(详细注释)

    /**
     * 返回Set的迭代器
     * @return 按升序排列的迭代器
     * 
     * 迭代器特点:
     * 1. 弱一致性:反映创建时的状态快照
     * 2. 不会抛出ConcurrentModificationException
     * 3. 线程安全:可以在并发环境下使用
     * 4. 有序性:按照元素的排序规则遍历
     * 5. 效率高:利用底层Map的迭代器实现
     * 
     * 使用场景:
     * - 遍历所有元素
     * - 顺序处理元素
     * - 在并发环境中的安全遍历
     */
    public Iterator<E> iterator() {
        return m.navigableKeySet().iterator();
    }

    /**
     * 返回Set的降序迭代器
     * @return 按降序排列的迭代器
     * 
     * 降序迭代器特点:
     * 1. 从最大元素开始遍历
     * 2. 按照排序规则的逆序遍历
     * 3. 同样具有弱一致性
     * 4. 线程安全且高效
     * 
     * 使用场景:
     * - 反向遍历元素
     * - 获取最大元素及以下的元素
     * - 实现降序处理逻辑
     */
    public Iterator<E> descendingIterator() {
        return m.descendingKeySet().iterator();
    }

    /**
     * 将Set转换为数组
     * @return 包含所有元素的数组
     * 
     * 转换特点:
     * 1. 保持元素的排序顺序
     * 2. 线程安全的操作
     * 3. 返回新的数组对象
     * 4. 不影响原Set的状态
     * 
     * 实现方式:
     * 1. 调用底层Map的keySet().toArray()
     * 2. 利用Map的有序特性保证数组顺序
     */
    public Object[] toArray() {
        return m.keySet().toArray();
    }

    /**
     * 将Set转换为指定类型的数组
     * @param a 指定类型的数组
     * @return 转换后的数组
     * @throws ArrayStoreException 如果数组类型不兼容
     * @throws NullPointerException 如果a为null
     * 
     * 类型转换特点:
     * 1. 保持元素的排序顺序
     * 2. 类型安全的转换
     * 3. 可能创建新的数组实例
     * 4. 保持线程安全性
     */
    public <T> T[] toArray(T[] a) {
        return m.keySet().toArray(a);
    }

4. 导航操作方法(详细注释)

    /**
     * 返回用于排序的比较器
     * @return 比较器,如果使用自然排序则返回null
     * 
     * 比较器特点:
     * 1. 直接返回底层Map的比较器
     * 2. 保持一致性
     * 3. 线程安全
     * 4. O(1)时间复杂度
     */
    public Comparator<? super E> comparator() {
        return m.comparator();
    }

    /**
     * 返回第一个(最小)元素
     * @return 第一个元素
     * @throws NoSuchElementException 如果Set为空
     * 
     * 查找过程:
     * 1. 调用底层Map的firstKey方法
     * 2. 利用跳表的特性快速定位
     * 3. 时间复杂度O(log n)
     * 
     * 异常处理:
     * - 当Set为空时抛出NoSuchElementException
     * - 保证操作的原子性
     */
    public E first() {
        return m.firstKey();
    }

    /**
     * 返回最后一个(最大)元素
     * @return 最后一个元素
     * @throws NoSuchElementException 如果Set为空
     * 
     * 查找过程:
     * 1. 调用底层Map的lastKey方法
     * 2. 利用跳表的特性快速定位
     * 3. 时间复杂度O(log n)
     * 
     * 使用场景:
     * - 获取最大元素
     * - 实现边界检查
     * - 优化范围查询
     */
    public E last() {
        return m.lastKey();
    }

    /**
     * 返回小于等于指定元素的最大元素
     * @param e 指定元素
     * @return 小于等于e的最大元素,如果不存在则返回null
     * 
     * Floor操作特点:
     * 1. 利用底层Map的floorKey方法
     * 2. 保持O(log n)的时间复杂度
     * 3. 线程安全的操作
     * 4. 支持null返回值
     * 
     * 应用场景:
     * - 范围查询
     * - 边界查找
     * - 实现floor函数
     */
    public E floor(E e) {
        return m.floorKey(e);
    }

    /**
     * 返回大于等于指定元素的最小元素
     * @param e 指定元素
     * @return 大于等于e的最小元素,如果不存在则返回null
     * 
     * Ceiling操作特点:
     * 1. 利用底层Map的ceilingKey方法
     * 2. 保持O(log n)的时间复杂度
     * 3. 线程安全的操作
     * 4. 支持null返回值
     * 
     * 应用场景:
     * - 范围查询
     * - 边界查找
     * - 实现ceiling函数
     */
    public E ceiling(E e) {
        return m.ceilingKey(e);
    }

    /**
     * 返回小于指定元素的最大元素
     * @param e 指定元素
     * @return 小于e的最大元素,如果不存在则返回null
     * 
     * Lower操作特点:
     * 1. 利用底层Map的lowerKey方法
     * 2. 保持O(log n)的时间复杂度
     * 3. 线程安全的操作
     * 4. 不包含等于的情况
     * 
     * 应用场景:
     * - 严格小于的查找
     * - 前驱元素查找
     * - 实现lower函数
     */
    public E lower(E e) {
        return m.lowerKey(e);
    }

    /**
     * 返回大于指定元素的最小元素
     * @param e 指定元素
     * @return 大于e的最小元素,如果不存在则返回null
     * 
     * Higher操作特点:
     * 1. 利用底层Map的higherKey方法
     * 2. 保持O(log n)的时间复杂度
     * 3. 线程安全的操作
     * 4. 不包含等于的情况
     * 
     * 应用场景:
     * - 严格大于的查找
     * - 后继元素查找
     * - 实现higher函数
     */
    public E higher(E e) {
        return m.higherKey(e);
    }

5. 元素提取和删除方法(详细注释)

    /**
     * 检索并移除第一个(最小)元素
     * @return 第一个元素
     * @throws NoSuchElementException 如果Set为空
     * 
     * Poll操作特点:
     * 1. 原子性操作:查找和删除同时进行
     * 2. 利用底层Map的pollFirstEntry方法
     * 3. 时间复杂度O(log n)
     * 4. 线程安全
     * 
     * 使用场景:
     * - 优先队列实现
     * - 获取并移除最小元素
     * - 实现poll语义
     */
    public E pollFirst() {
        Map.Entry<E,Object> e = m.pollFirstEntry();
        return (e == null) ? null : e.getKey();
    }

    /**
     * 检索并移除最后一个(最大)元素
     * @return 最后一个元素
     * @throws NoSuchElementException 如果Set为空
     * 
     * Poll操作特点:
     * 1. 原子性操作:查找和删除同时进行
     * 2. 利用底层Map的pollLastEntry方法
     * 3. 时间复杂度O(log n)
     * 4. 线程安全
     * 
     * 使用场景:
     * - 获取并移除最大元素
     * - 实现降序poll语义
     * - 双端队列操作
     */
    public E pollLast() {
        Map.Entry<E,Object> e = m.pollLastEntry();
        return (e == null) ? null : e.getKey();
    }

6. 范围操作方法(详细注释)

    /**
     * 返回此set的部分视图,元素范围从fromElement到toElement
     * @param fromElement 起始元素(包含)
     * @param toElement 结束元素(不包含)
     * @return 指定范围的子集
     * 
     * 子集特点:
     * 1. 视图语义:与原Set共享数据
     * 2. 动态更新:原Set的修改会反映到子集中
     * 3. 边界检查:自动处理边界条件
     * 4. 线程安全:继承原Set的线程安全性
     * 
     * 使用场景:
     * - 范围查询
     * - 数据分片
     * - 局部操作
     */
    public NavigableSet<E> subSet(E fromElement,
                                  boolean fromInclusive,
                                  E toElement,
                                  boolean toInclusive) {
        return new ConcurrentSkipListSet<E>
            (m.subMap(fromElement, fromInclusive, toElement, toInclusive));
    }

    /**
     * 返回此set的部分视图,元素小于toElement
     * @param toElement 结束元素(不包含)
     * @param inclusive 是否包含结束元素
     * @return 指定范围的头部集
     * 
     * 头部集特点:
     * 1. 从最小元素开始
     * 2. 到指定元素结束
     * 3. 支持包含/不包含边界
     * 4. 保持有序性
     */
    public NavigableSet<E> headSet(E toElement, boolean inclusive) {
        return new ConcurrentSkipListSet<E>(m.headMap(toElement, inclusive));
    }

    /**
     * 返回此set的部分视图,元素大于等于fromElement
     * @param fromElement 起始元素(包含)
     * @param inclusive 是否包含起始元素
     * @return 指定范围的尾部集
     * 
     * 尾部集特点:
     * 1. 从指定元素开始
     * 2. 到最大元素结束
     * 3. 支持包含/不包含边界
     * 4. 保持有序性
     */
    public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
        return new ConcurrentSkipListSet<E>(m.tailMap(fromElement, inclusive));
    }

    /**
     * 返回此set的部分视图,元素范围从fromElement到toElement
     * @param fromElement 起始元素(包含)
     * @param toElement 结束元素(不包含)
     * @return 指定范围的有序子集
     */
    public SortedSet<E> subSet(E fromElement, E toElement) {
        return subSet(fromElement, true, toElement, false);
    }

    /**
     * 返回此set的部分视图,元素小于toElement
     * @param toElement 结束元素(不包含)
     * @return 指定范围的有序头部集
     */
    public SortedSet<E> headSet(E toElement) {
        return headSet(toElement, false);
    }

    /**
     * 返回此set的部分视图,元素大于等于fromElement
     * @param fromElement 起始元素(包含)
     * @return 指定范围的有序尾部集
     */
    public SortedSet<E> tailSet(E fromElement) {
        return tailSet(fromElement, true);
    }

7. 克隆方法(详细注释)

    /**
     * 克隆ConcurrentSkipListSet
     * @return 克隆后的ConcurrentSkipListSet
     * 
     * 克隆特点:
     * 1. 浅克隆:只克隆Set结构,不克隆元素
     * 2. 独立状态:新Set有自己的底层Map
     * 3. 相同元素:包含相同的元素引用
     * 4. 相同排序:保持相同的排序规则
     * 5. 线程安全:克隆过程线程安全
     * 
     * 克隆过程:
     * 1. 创建新的ConcurrentSkipListSet实例
     * 2. 克隆底层的ConcurrentNavigableMap
     * 3. 保持原有的排序和元素
     */
    public Object clone() {
        try {
            @SuppressWarnings("unchecked")
            ConcurrentSkipListSet<E> clone =
                (ConcurrentSkipListSet<E>) super.clone();
            clone.m = new ConcurrentSkipListMap<E,Object>(m);
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new InternalError();
        }
    }

8. Spliterator(Java 8新增)

    /**
     * 返回Spliterator,用于并行处理
     * @return Spliterator
     * 
     * Spliterator特点:
     * 1. 支持并行遍历
     * 2. 有序性:保持元素的排序顺序
     * 3. 线程安全:可以在并发环境下使用
     * 4. 高效性:优化的分割算法
     * 
     * 并行处理优势:
     * - 提高大数据集的处理效率
     * - 充分利用多核CPU
     * - 减少处理时间
     */
    public Spliterator<E> spliterator() {
        return m.keySet().spliterator();
    }

9. ConcurrentSkipListSet 的特点分析

核心设计理念:

/**
 * ConcurrentSkipListSet的核心设计思想:
 * 
 * 1. 委托模式:
 *    - 基于ConcurrentSkipListMap实现
 *    - 利用Map的键存储Set元素
 *    - 值使用统一的虚拟对象
 *    - 简化实现,复用成熟代码
 * 
 * 2. 有序性保证:
 *    - 元素按照自然顺序或比较器排序
 *    - 支持导航操作
 *    - 提供有序的迭代器
 *    - 保持Set的唯一性约束
 * 
 * 3. 线程安全性:
 *    - 继承底层Map的线程安全性
 *    - 所有操作都是原子的
 *    - 支持高并发读写
 *    - 无锁设计(通过底层Map实现)
 * 
 * 4. 高效性:
 *    - 平均O(log n)的操作性能
 *    - 支持范围查询
 *    - 优化的内存使用
 *    - 良好的并发性能
 * 
 * 5. 功能完整性:
 *    - 完整的Set操作
 *    - 丰富的导航功能
 *    - 灵活的范围操作
 *    - 支持子集视图
 * 
 * 适用场景:
 * - 需要有序Set的高并发场景
 * - 频繁进行范围查询的场景
 * - 需要导航操作的场景
 * - 要求线程安全的有序集合
 */

性能特征分析:

/**
 * ConcurrentSkipListSet的性能特征:
 * 
 * 时间复杂度:
 * - add(e): O(log n) 平均情况
 * - remove(o): O(log n) 平均情况
 * - contains(o): O(log n) 平均情况
 * - size(): O(1) 平均情况
 * - first()/last(): O(log n) 平均情况
 * - floor(e)/ceiling(e): O(log n) 平均情况
 * - lower(e)/higher(e): O(log n) 平均情况
 * - pollFirst()/pollLast(): O(log n) 平均情况
 * 
 * 空间复杂度:
 * - O(n) 基本存储空间
 * - 额外的索引节点开销(跳表特性)
 * - 每个元素一个统一的PRESENT对象
 * - 子集视图的额外开销
 * 
 * 并发特性:
 * - 完全线程安全
 * - 无锁设计(通过底层Map实现)
 * - 支持高并发读写
 * - 弱一致性:读操作可能看不到最新的写操作
 * - 不会抛出ConcurrentModificationException
 * 
 * 内存使用:
 * - 动态分配,按需扩展
 * - 共享PRESENT对象节省内存
 * - 及时GC,避免内存泄漏
 * - 子集视图为视图语义,不复制数据
 * 
 * 适用性:
 * - 读操作频繁:性能优秀
 * - 写操作频繁:性能良好
 * - 范围查询:性能优秀
 * - 有序遍历:性能优秀
 */

10. 使用示例和最佳实践

/**
 * 使用示例:
 * 
 * // 基本使用
 * ConcurrentSkipListSet<String> set = new ConcurrentSkipListSet<>();
 * 
 * // 添加元素
 * set.add("apple");
 * set.add("banana");
 * set.add("cherry");
 * set.add("apple"); // 重复元素,不会添加
 * 
 * System.out.println("Set size: " + set.size()); // 输出: 3
 * System.out.println("Contains apple: " + set.contains("apple")); // 输出: true
 * 
 * // 导航操作
 * String first = set.first(); // "apple"
 * String last = set.last();   // "cherry"
 * String floor = set.floor("blueberry"); // "banana"
 * String ceiling = set.ceiling("blueberry"); // "cherry"
 * 
 * // 范围操作
 * NavigableSet<String> subSet = set.subSet("apple", true, "cherry", false);
 * System.out.println("SubSet: " + subSet); // [apple, banana]
 * 
 * // 迭代器使用
 * System.out.println("Forward iteration:");
 * for (String element : set) {
 *     System.out.println(element);
 * }
 * 
 * System.out.println("Backward iteration:");
 * Iterator<String> descendingIt = set.descendingIterator();
 * while (descendingIt.hasNext()) {
 *     System.out.println(descendingIt.next());
 * }
 * 
 * // 并发使用示例
 * ExecutorService executor = Executors.newFixedThreadPool(10);
 * 
 * // 并发添加
 * for (int i = 0; i < 10; i++) {
 *     final int threadId = i;
 *     executor.submit(() -> {
 *         for (int j = 0; j < 1000; j++) {
 *             set.add("thread" + threadId + "_item" + j);
 *         }
 *     });
 * }
 * 
 * // 并发查询
 * for (int i = 0; i < 10; i++) {
 *     executor.submit(() -> {
 *         for (int j = 0; j < 1000; j++) {
 *             boolean contains = set.contains("someElement");
 *             // 处理结果
 *         }
 *     });
 * }
 * 
 * 最佳实践:
 * 
 * 1. 合理选择元素类型:
 *    // 元素应该实现Comparable接口或提供Comparator
 *    ConcurrentSkipListSet<Integer> numberSet = new ConcurrentSkipListSet<>();
 *    
 *    // 或者使用自定义比较器
 *    Comparator<String> comparator = String.CASE_INSENSITIVE_ORDER;
 *    ConcurrentSkipListSet<String> stringSet = 
 *        new ConcurrentSkipListSet<>(comparator);
 * 
 * 2. 避免null元素:
 *    // ConcurrentSkipListSet不允许null元素
 *    try {
 *        set.add(null); // 抛出NullPointerException
 *    } catch (NullPointerException e) {
 *        System.out.println("Null elements are not allowed");
 *    }
 * 
 * 3. 正确使用导航操作:
 *    // 利用导航操作提高查询效率
 *    String lower = set.lower("target");
 *    String higher = set.higher("target");
 *    if (lower != null) {
 *        System.out.println("Lower element: " + lower);
 *    }
 *    if (higher != null) {
 *        System.out.println("Higher element: " + higher);
 *    }
 * 
 * 4. 合理使用范围操作:
 *    // 创建范围视图进行局部操作
 *    NavigableSet<String> range = set.headSet("m");
 *    System.out.println("Elements less than 'm': " + range.size());
 *    
 *    // 范围视图与原Set共享数据
 *    range.add("newElement"); // 也会添加到原Set中
 * 
 * 5. 正确处理poll操作:
 *    // poll操作会同时检索和移除元素
 *    String firstElement = set.pollFirst();
 *    if (firstElement != null) {
 *        System.out.println("Polled element: " + firstElement);
 *    }
 * 
 * 6. 监控Set状态:
 *    // 定期检查Set的大小和状态
 *    int size = set.size();
 *    boolean isEmpty = set.isEmpty();
 *    System.out.println("Set size: " + size + ", Is empty: " + isEmpty);
 *    
 *    // 注意:在高并发环境下size()可能是估算值
 * 
 * 7. 使用子集视图:
 *    // 创建子集视图进行局部操作
 *    SortedSet<String> subset = set.subSet("a", "z");
 *    System.out.println("Subset size: " + subset.size());
 *    
 *    // 子集操作会反映到原Set中
 *    subset.add("newElement");
 *    System.out.println("Original set size: " + set.size());
 * 
 * 8. 注意内存使用:
 *    // 大量数据时注意内存使用
 *    if (set.size() > 100000) {
 *        System.out.println("Large set size, consider memory usage");
 *    }
 * 
 * 9. 正确使用迭代器:
 *    // 迭代器是弱一致性的
 *    Iterator<String> it = set.iterator();
 *    while (it.hasNext()) {
 *        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值