Collection和Map的那些常用的类操作的实现原理简要理解笔记

Collection集合常用实现类及方法 目录 ArrayList有序,底层数组实现,遍历轮询效率高 LinkedList 链表形式存储,增删效率高 HashSet 无序,不重复(通过equals()和hashCode方法过滤是否重复) TreeSet无序,不重复(通过实现Comparable的CompareTo方法过滤重复元素) ArrayList有序,底层数组实现,遍历轮询效率高 增: boolea... 阅读详情

内容更新中……

Java集合框架
集合类

  • Collection(interface)

    • List(interface)
      • ArrayList:数组实现,适合随机访问元素
      • LinkedList(实现了Queue接口):链表实现,适合插入、删除、移动
      • Vector(与ArrayList相比多了个线程安全)
    • Set(interface)

      • HashSet(使用散列函数)——> 通过HashMap实现,add(E e)是通过HashMap的add(K,V)方法实现,E是K;remove(Object o)是通过HashMap的remove(Object o)实现的。按哈希值保存其值,通过Iterator迭代器访问hashset。
      • TreeSet(使用红黑树)——> 通过TreeMap实现。保存的值是排序的。
      • LinkedHashSet(链表结合散列函数)。继承了HashSet,通过LinkedHashMap实现的。值是按照插入的顺序存储的。(结构有HashMap,和LinkList。双向链表存储插入的Entry元素。)
    • Queue(interface)

  • Map(interface)

    • HashMap(可以存放null)
    • TreeMap(不可以存放null,排序功能)
    • HashTable(不可以存放null,与HashMap比多了个线程安全)

其中线程安全类除了上面说的Vector和HashTable外,还有Stack、Enumeration是线程安全类。
除此集合类外,线程安全的类还有StringBuffer等。

线程安全是指在访问方法时不允许别的线程去改变,并不是说一系列操作是同步的。
比如StringBuffer的apeend方法线程安全的,但是调用多次append方法可能会对最后的结果造成顺序上的差别(append单句是顺序的)。

几个类和接口的比较
Collection:接口,各种集合结构的父接口
Collections:专用静态类,包含各种有关集合操作的静态方法。提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
Array:Java中最基本的一个存储结构,提供动态创建和访问Java数组的方法。元素类型必须相同。效率高,但容量固定无法改变。无法判断实际多少元素。
Arrays:静态类。专门用来操作数组,提供搜索、排序、复制等静态方法。

HashMap的设计:
数据结构中有数组和链表来存数据。
数组的优缺点:访问迅速,插入和删除难
链表的优缺点:插入和删除简单,访问复杂度高
这确实是两个极端。

如何综合数组和链表的特性,做出访问容易,插入和删除都容易的数据结构?
可以,就是哈希表(HashTable):拉链法,使用数组,数组里面是链表(链表的数组,数组中存的是链表的头结点,就像邻接表一样)
这些元素是通过散列公式存进去的。

HashMap也是一个线性的数组实现的,其存储的容器就是一个线性数组。(如何用线性数组来存取键值对呢)
首先HashMap里面有一个静态内部类Entry,其重要的属性有key,value,next,从key,value看出来Entry是HashMap键值对实现的一个基础bean,HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。
transient Entry[] table;

HashMap的存取实现

//存储时
int hash = key.hashCode();
int index = hash % Entry[].length;
Entry[hash] = value;
//取值时
int hash = key.hashCode;
int index = hash % Entry[].length;
return Entry[index];

Put操作
如果两个key通过hash%Entry[].length得到的index相同会不会有覆盖的风险(两个不同的key拥有相同的index)。
因为有个next属性,作用是指向下一个Entry。如果A的index是0,B的index也是0,那么进来B的时候,A的next是B。index = 0的位置其实是放了三个键值对的。数组中存储的是最后插入的元素。

public V put(K key, V value){
     if(k == null)
          return putForNullKey(value);
     int hash = hash(key.hashCode());
     int i = indexFor(hash,table.length);
     //遍历链表,看有没有相同的,就是拉链法的那个拉链后面的东西
     for(Entry<K,V> e = table[i] ; e != null ; e = e.next){
          Object k;
          if(e.hash == hash && ((k = e.key) == key || key.equals(k)) )//如果key在链表中已存在,替换为新的value{
               V oldValue = e.value;
               e.value = value;
               e.recordAccess(this);
               return oldValue;
          }
     }
     modCount++;
     addEntry(hash,key,value,i);
     return null;
}

void addEntry(int hash ,K key , V value, int buketIndex)
{     
     Entry<K,V> e = table[buketIndex];
     table[bucketIndex] = new Entry<K,V>(hash,key,value,e);//e是Entry.next
     if(size++ >= threshold)
          resize(2*table.length);
}

HashMap里面包含一些优化方面的实现,比如Entry数组的长度一定后,随着map里面数据的越来越长,这样同一个index的链长度就会很长,网HashMap里面设置一个引子,随着map的size越来越大,Entry[]会以一定的规则加长长度。

说完HashMap后,从源码来简单看看Collection的子接口List接口的实现类ArrayList的添加和删除元素到底是怎么操作的

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

直接添加元素就是先判断是否需要扩容,然后直接把元素添加到末尾

    public void add(int index, E element) {
        rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

先看这个添加的函数,在索引index处添加元素E element。
首先是确定index的范围,然后根据当前的size+1判断是否需要扩容(扩容机制:看当前的arraylist是否为空,如果为空就是取默认容量和传入参数的较大值,然后调用enSureExplicitCapacity方法进行扩容。
modCount++,然后看传入的size+1参数是否大于add之前数组 elementData 的大小,如果是,就调用grow方法进行扩容 newCapacity=oldCapacity+(oldCapacity>>1),扩大1.5倍,然后将扩容后的数组复制一份Array.copyof给 elementData )
再者就是使用native方法arraycopy去对数组进行一个复制。其声明如下:

public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

将elementData数组第index个元素后的元素拷到elementData从第index+1位置到后面的位置,长度是size-index。其实做的就是后移操作。

    public E remove(int index) {
        rangeCheck(index);
        modCount++;
        E oldValue = elementData(index);
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,numMoved);
        elementData[--size] = null; // clear to let GC do its work
        return oldValue;
    }

至于这个移除第index个位置的元素操作,依旧先检查index的范围,修改次数modCount加1,然后获取要删除索引的元素的值。
然后就是数组往前移动覆盖到index的值,如果移动的长度要大于0,则使用System.arraycopy 的方法去复制数组;然后把数组的最后一个元素置空,让GC去回收它,返回的是一个旧的元素的值。

(PS:在一个迭代器初始的时候会赋予它调用这个迭代器的对象的mCount,如何在迭代器遍历的过程中,一旦发现这个对象的mcount和迭代器中存储的mcount不一样那就抛异常。
下面是这个mcount相关机制的解释引用:
Fail-Fast 机制
我们知道 java.util.HashMap 不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。这一策略在源码中的实现是通过 modCount 域,modCount 顾名思义就是修改次数,对HashMap 内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的 expectedModCount。在迭代过程中,判断 modCount 跟 expectedModCount 是否相等,如果不相等就表示已经有其他线程修改了 Map:注意到 modCount 声明为 volatile,保证线程之间修改的可见性。所以当大家遍历那些非线程安全的数据结构时,尽量使用迭代器)

由此也可以联想在遍历ArrayList的过程中,不能add和remove元素,此时会报错误
java.util.ConcurrentModificationException

    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

再看一下这个移除object的操作,如果要移除的对象为空,List是可以添加null的。所以需要遍历list,然后根据index去查看如果有相同的那么就用新的数组去覆盖原来旧的数组。

    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

移除元素操作用到了快速移除fastRemove方法,与remove(int index)方法类似,只是不需要检查index的范围,因为这个index已经确定好是存在的。

    public void clear() {
        modCount++;
        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;
        size = 0;
    }

clear元素很简单,全部置空然后长度归0

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

往ArrayList中添加实现Collection的类,先转换成Object数组,然后判断当前默认值和传入参数(两个list的长度和)确定是否需要扩容,再把新的数组a复制到数组的末尾。

    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);
        Object[] a = c.toArray(); // c可以转换成数组
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,numMoved);
        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

在指定位置index中往ArrayList中添加实现Collection的类,先转换成Object数组,然后判断当前默认值和传入参数(两个list的长度和)确定是否需要扩容,把原list数组index及以后的数据移到index+numNew(要添加数组的长度)处,再把新的数组a复制到数组的index处,再更新新list的size。

public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }

removeAll首先需要判断要移除的c是否为空,如果为空,抛出空指针异常。

removeAll基于batchRemove来实现的,batchRemove源码如下:

private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

先将当前的数据备份到同步局部变量final数组elementData(这个数组不能指向其它数组对象,内容可以变),设置读写两个指针r,w。遍历当前list,把当前list中除了要移除的元素之外的元素保存到elementData中。如果没能遍历完list(可能c.contains抛出异常),将未能够遍历的元素(index为r之后)全部复制到w指位置后(类似于未能遍历元素的恢复)。w指针更新位置(w位置和w之前的元素就是新数组的元素)。如果w位置不在list末尾,说明元素改动过了(至少有一次c.contains为true),此时将w后面多余的元素删除置空,让GC回收它们。

PHP实现Collection数据集类及其原理 本文目录 : Collection源码讲解与例子 ArrayAccess的使用JsonSerializable的使用Countable的使用IteratorAggregate、ArrayIterator的使用内置函数的使用 PHP 语言最重要的特性之一便是数组了(特别是关联数组)。 PHP 为此也提供不少的函数和类接口方便于数组操作,但没有一个集大成的类专门用来操作 阅读详情

相关推荐

Collection 与 Map

当大量 key 的 hashCode 冲突落到同一个桶,会形成长链表,查找从 O(1) 退化到 O(n)。(native 方法,底层有优化),加上 CPU 缓存友好,多数场景反而更快。需要注意的是,"Set 都是无序集合"这个说法并不准确,TreeSet 就是有序的。,锁细化到单个桶,并发度更高。是高并发场景的首选,采用分段锁/桶级锁设计,性能远强于 Hashtable。是线程安全版的 ArrayList,因为锁粒度太粗,现在基本无人使用。最常用,基于哈希表,键值对无序,key 不可重复,

m0_68935893的博客 214

分析Collection(一)常用的实现类

通过源代码分析CollectionConllection(集合)概念集合与数组的差别数据结构集合框架之间的关系Collection 下的接口(常用的)1.ListArrayList数据结构优点缺点LinkedList数据结构优点缺点Vector2.SetHashSetTreeSetCollection中定义的方法 Conllection(集合)概念 在Java中我们通常使用OOP来编写程序,所以,在这个面向对象编程中最小的单位是一个对象(类),为了方便使用多个对象,我们将对象进行了存储,我们在使用中,最常见

Fanx繁星 776

Autosar中CAN信号传输的模块化解析与实战应用

本文深入解析了AUTOSAR架构下CAN信号传输的模块化流程,详细阐述了从应用层软件组件(SWC)到COM模块打包、PduR路由、CanIf硬件抽象直至Can Driver驱动的完整“流水线”。文章通过实战案例,重点说明了模块化设计如何实现解耦与可移植性,并提供了关键模块配置要点与调试技巧,旨在帮助开发者构建高可靠、实时的车载通信系统。

g9h0i1的博客 902

Java集合Collection之实现原理解读(Map)

一、简介 除了List,平时在项目中用的比较多的还有Map,Map是存放键值对的数据结构,键不允许重复,如果键重复会覆盖之前的值。比较常用到的有: HashMap和HashTable。本文将会模仿Map的源码实现两个自定义的Map(一个是没有用hashcode的效率低的版本,一个是用hashcode优化版),只是帮助理解Map底层实现原理,实际项目中没有必要去自定义Map。 二、实现原理 M...

weixiaohuai的博客 1435

IS-IS协议深度解析(二):LSDB同步机制与骨干网实战部署

本文深度解析IS-IS协议的LSDB同步机制与骨干网实战部署。详细阐述了DIS角色如何通过CSNP/PSNP报文高效管理链路状态数据库同步,并与OSPF DR机制进行对比。重点介绍了Level-2骨干网作为逻辑高速公路系统的工作原理,以及通过ATT比特位实现跨区域路由的自动化。最后提供了高可靠性骨干网的实战配置要点与验证方法。

kite3的博客 644

Ubuntu22.04系统安装及配置

下载Ubuntu22.04系统镜像(下载地址:链接:https://caiyun.139.com/m/i?提取码:fLRJ),结合rufus和系统镜像制作USB启动盘,在物理机服务器上通过USB启动盘进行安装;或通过Esxi虚拟化平台创建虚拟机,然后在虚拟机中使用系统镜像进行安装。

cheagoun的博客 8万+

了解Collection的实现类(List,Set,Map)

了解Collection的实现类(List,Set,Map)

packge的博客 1022

控制障碍函数(Control Barrier Function,CBF) 一、定义

介绍控制障碍函数

Lyajpunov的博客 2万+

Java - Collection 接口及主要实现类

Collection 接口及主要实现类一、Collection1. 基本介绍2. 常用方法二、List1. 基本介绍2. List 的特有方法3. List 接口的实现类三、Set 接口1. 基本介绍2. Set 接口的实现类 一、Collection 1. 基本介绍 Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是 java.util.List 与 java.util.Set。其中: List 的特点是有索引、存取有序、可重复。 Set 的特点是无

xing123456jl的博客 1万+

非线性动力学_用MatCont分析非线性动力学方程组(一)

写在前面:笔者打算写两到三篇系列文章介绍MatCont,此为第一篇《用MatCont分析非线性动力学方程组(一)》。MatCont是一个基于Matlab而开发的免费工具包,专门用来做非线性动力学模型分叉、向量场、周期性等分析,符号(连续性)运算和数值(离散性)运算均适用。该工具的开发及研究项目由比利时Ghent大学的Willy Govaerts教授、荷兰Utrecht大学的Yuri Kuznets...

weixin_39700215的博客 2766

电子工程师必看:2024年IPC-A-610J和J-STD-001J新标准到底更新了啥?

本文深入解析2024年IPC-A-610J和J-STD-001J新标准的核心更新,重点关注电子制造领域的验收标准与工艺参数变化。新版标准强化了高密度组装和微焊接工艺的规范,包括焊料润湿角调整、BGA空洞率严格化等关键变更,为电子工程师提供实用指南。同时关联IPC-7711/21D和IPC-2221C的配套更新,助力企业快速适应新标准要求。

weixin_33694620的博客 779

java常用Collection和Map的继承结构图

1、常用Collection继承结构图及说明: 1.1 哈希表/散列表数据结构 一、关于Collection常用方法: ( 新建Collection: Collection co1 = new ArrayList();// 多态 Collection co2 = new LinkedList();// 多态 Collection co3 = new Vector();// 多态 ) 1. boolean add(E e) : ..

qq_35661527的博客 1191

使用Avalonia跨Linux平台

Avalonia类似使用WPF的技术方法来开发,所以,需要会使用WPF,其中的一些语法和写法与WPF有所区别,但是他们相差不大,会WPF的话,基本上可以看看文档后,完全掌握Avalonia的使用,从而达到跨平台开发的任务。其实写法有些不一样,可以参考官网的说明,进行修改。2. 安装完成后,重启VS2022,我们可以看到1和2,其中1是使用MVVM模式开发,2是普通模式开发,我们选择2,建立项目。9.直接双击粉红色的文件,进行安装,输入密码,但是美中不足的是,好像没有图标,图标似乎需要单独去设置。

故里2130 1万+

类集、Collection集合、Map

类集、集合一、类集设置的目的二、Collection接口1.List接口1.1 ArrayList1.2 LinkedList2.Iterator迭代器3.for each(增强for循环)4.Set接口4.1 HashSet 集合4.2 HashSet 存储数据的结构(哈希表)4.3 TreeSet4.4 使用HashSet 区分自定义类的重复元素4.5 总结三、 集合工具类Collections四、Comparator 比较器1. 默认排序规则的定义2. 简述Comparable和Comparator两

weixin_41792829的博客 243

OpenCode + Oh My OpenCode:打造你的终极AI编程团队(OpenCode新手入门指南)

本文详细介绍了开源终端AI编程助手 OpenCode 的完整配置教程 🖥️,从安装、AI模型配置到增强插件 Oh My OpenCode 的部署一应俱全。支持 Claude、ChatGPT、Gemini 多模型切换 🤖,还能实现多AI智能体协作开发。步骤清晰适合新手 📝,想在终端用AI写代码的开发者不要错过 ✨

XiaoqiangClub的博客 1万+

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Collection和Map简要总结对比

一、Collection List(存取有序,有索引,可以重复)        ArrayList           底层是数组实现的,线程不安全,查找和修改快,增和删比较慢        LinkedList           底层是链表实现的,线程不安全,增和删比较快,查找和修改比较慢        Vector           底层是数组实现的,线程安全的,无论增删改...

qq_40298054的博客 276

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Java——集合中的Collection接口通过ArrayList类实现一些常用的方法

Java——集合中的Collection接口通过ArrayList类实现一些常用的方法

贝尔摩德苦艾酒 9689

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

集合总结(Collection,List,Set,Map)(补充集合结构图的关系)

集合集合体系结构图Collection集合概述常用方法遍历(迭代器)及代码演示内存图概述List集合Set集合Set集合三级目录 集合体系结构图 Collection集合 概述 1.单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素 2.JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现 常用方法 遍历(迭代器)及代码演示 内存图 概述 List集合 Set集合 Set集合 三级目录 ...

m0_59619191的博客 3333

Java集合Collection、List、Set、Map使用详解

Java集合排序及java集合类详解 (Collection, List, Set, Map)   摘要内容 集合是Java里面最常用的,也是最重要的一部分。能够用好集合和理解好集合对于做Java程序的开发拥有无比的好处。本文详细解释了关于Java中的集合是如何实现的,以及他们的实现原理。   目 录 1        集合框架............................

gaoxiangnumber1——Gao Xiang's Blog 2961

Java 容器Collection 和 Map

Collection List ArrayList LinkedList Vector Stack Set HashSet LinkedHashSet TreeSet Map HashMap LinkedHashMap TreeMap ConcurrentHashMap Hashtable 1、Collection 和 Collections 有...

weixin_42179326的博客 186

Java 集合框架(Collection+Map)

1. 什么是Collection?            

qq_40668700的博客 1005

【java】集合,List,Set,Map和Collection接口,ArrayList类,LinkedList类,HashSet类,TreeSet类

【集合中主要接口的概述】: 在java.until包中提供了一些集合,常用的有List,Set和Map,其中List和Set实现了Collection接口,这些集合又被称为容器,它们与数组不同,数组的长度是固定的。但是集合的长度是可变的,数组是用来存放基本数据类型的数据,集合用来存放类对象的引用。 【List,Set,Map和Collection接口的主要特征如下】: 1、Collectio...

Wweakness的博客 539

Java中Collection和Map的区别和联系——主要是概念性和理解性的东西

java中的几个集合框架的设计思想和底层逻辑始终容易忘,而且容易分不清楚这里自己写一下做个整合,主要是概念性的东西,具体代码和各自的方法没有描述。

weixin_42761614的博客 552

JavaSE 集合(Collection子接口之三:Map接口)

一.Map接口的框架: |----Map:双列数据,存储key-value对的数据 ---类似于高中的函数:y=f(x),python中的字典 |-----HashMap:作为Map的主要实现类:线程不安全的,效率高:存储null的key和value |-----LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历; 原因:在原有的HashMap底层结构基础上,添加一对指针,指向前一个和后一个元素。 |-----TreeMap:保证按照添加的key-value对

weixin_44717746的博客 844

浅谈JAVA类集框架(JAVA集合类)【学习笔记】

Java知识中有一个类集框架的概念,每种文献各有各的说法,也有的叫做Java集合类,造成混乱。并且概念中Collection翻译成中文又是类集的意思,Set翻译成中文是集合的意思,混乱的翻译、指代不明的名词概念以及网络上copy的差错,导致初学者从入门到放弃。现在本学习笔记将帮你梳理基础概念、供你参考解决疑惑。

liangcheng0523的博客 1万+
上一篇: Activity和Fragment之间的联系和区别
下一篇: HTK语音识别之MFCC参数基本配置参数解释
llearner
博客等级 码龄11年 23粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值