java-----集合与映射

一、集合

1、集合的由来

Java中给我们提供了另外一类容器,专门用来存放对象,这个容器就是我们要学习的集合。

集合和数组既然都是容器,它们有啥区别呢?
数组的长度是固定的。集合的长度是可变的。
数组中存储的是同一类型的元素,可以存储基本数据类型值。
集合存储的都是对象。而且对象的类型可以不一致。
Java的集合主要有List , Set, Map

其中 List , Set 继承至Collection接口,Map为独立接口

List下有ArrayList,LinkedList,Vector

Set下有HashSet,LinkedHashSet,TreeSet

Map下有HashMap,LinkedHashMap, TreeMap,Hashtable
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、Collection接口常用功能

Collection方法

1.boolean add(E e) :确保此 collection 包含指定的元素(可选操作)。
2.boolean addAll(Collection<? extends E> c) :将指定 collection 中的所有元素都添加到此collection 中(可选操作)。
3.void clear() :移除此 collection 中的所有元素(可选操作)。
4.boolean contains(Object o) :如果此 collection 包含指定的元素,则返回 true 。
5. boolean containsAll(Collection<?> c) :如果此 collection 包含指定 collection 中的所有元素,则返回 true 。
6. boolean equals(Object o) :比较此 collection 与指定对象是否相等。
7.int hashCode() :返回此 collection 的哈希码值。 boolean isEmpty() :如果此 collection 不包含元素,则返回 true 。 Iterator iterator() :返回在此 collection 的元素上进行迭代的迭代器。
8.boolean remove(Object o) : 从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。
9.boolean removeAll(Collection<?> c) :移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。
10.boolean retainAll(Collection<?> c) :仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。
11.int size() :返回此 collection 中的元素数。
12.Object[] toArray() :返回包含此 collection 中所有元素的数组。
13.T[] toArray(T[] a) :返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。

3、迭代器的使用

Iterator原理

由于集合容器有很多,每个容器都有自身的数据存储结构,即每个容器自身最清楚自己中数据是如何存储的,容器这么多,每个容器数据存储又不相同,这时就在它们之间找取出元素的共性进行了抽取,抽取出集合容器取出元素的共同特点。
在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
集合中把这种取元素的方式描述在Iterator接口中。

注意:在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会发生java.util.NoSuchElementException没有这个元素异常.

集合的使用细节

  1. 集合中存储其实都是对象的地址。
  2. 集合中可以存储基本数值吗?不行,但是jdk1.5以后可以这么写,但是存储的还是对象(基本数据类型包装类对象)。
  3. 存储时提升了Object。取出时要使用元素的特有内容,必须向下转型。

注意:如果集合中存放的是多个对象,这时进行向下转型会发生类型转换异常。

二、List

一个有序集合(也被称为序列)。此接口的用户在列表中的每个元素都被插入的地方有精确的控制。用户可以通过它们的整数索引(在列表中的位置)访问元素,并在列表中搜索元素。 与set不同的是,列表通常允许重复元素。

1.List方法

1.void add(int index, E element) :在列表的指定位置插入指定元素(可选操作)。
2.boolean addAll(int index, Collection<? extends E> c) :将指定 collection 中的所有元素都插入到列表中的指定位置。
3.E get(int index) :返回列表中指定位置的元素。
4.int indexOf(Object o) :返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
5.int lastIndexOf(Object o) :返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。
6. ListIterator listIterator() :返回此列表元素的列表迭代器(按适当顺序)。
7.E remove(int index) :移除列表中指定位置的元素(可选操作)。
8.E set(int index, E element) :用指定元素替换列表中指定位置的元素(可选9.操作)。
10.List subList(int fromIndex, int toIndex) :返回列表中指定的 fromIndex (包括 )和toIndex (不包括)之间的部分视图。
11.default void sort(Comparator<? super E> c) :分类列表使用提供的Comparator比较元素。

2、List实现子类

2.1ArrayList

List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。
每个 ArrayList 实例都有一个容量 。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。

2.2LinkedList

List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null )。
除了实现 List 接口外, LinkedList 类还为在列表的开头及结尾 get 、 remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。
此实现不是同步的

LinkedList用作双端队列
1.void addFirst(E e) :将指定元素插入此列表的开头。
2.void addLast(E e) :将指定元素添加到此列表的结尾。
3.E removeFirst() :移除并返回此列表的第一个元素。
4.E removeLast() :移除并返回此列表的最后一个元素。
5.E getFirst() :返回此列表的第一个元素。
6.E getLast() :返回此列表的最后一个元素。
7.boolean owerFirst(E e) :在此列表的开头插入指定的元素。
8.boolean owerLast(E e) :在此列表的末尾插入指定的元素。
9.E pollFirst() :获取并移除此列表的第一个元素;如果此列表为空,则返回 null 。
10. E pollLast() :获取并移除此列表的最后一个元素;如果此列表为空,则返回 null 。
11.E peekFirst() :获取但不移除此列表的第一个元素;如果此列表为空,则返回 null 。
12.E peekLast() :获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null 。

LinkedList用作队列
1.boolean ower(E e) :将指定元素添加到此列表的末尾(最后一个元素)。
2.E poll() :获取并移除此列表的头(第一个元素)。
3.E element() :获取但不移除此列表的头(第一个元素)。

LinkedList用作栈
1.void push(E e) :将元素推入此列表所表示的堆栈。
2.E pop() :从此列表所表示的堆栈处弹出一个元素。
3.E peek() :获取但不移除此列表的头(第一个元素)。

2.3Vector

优点: 底层数据结构是数组,查询快,增删慢。缺点: 线程安全,效率低
在这里插入图片描述

三、Queue接口常用功能

在这里插入图片描述

1. Queue实现子类

1.1ArrayDeque

从名字可以看出ArrayDeque底层通过数组实现,为了满足可以同时在数组两端插入或删除元素的需求,该数组还必须是循环的,即循环数组 (circular array),也就是说数组的任何一点都可能被看作起点或者终点。ArrayDeque是非线程安全的(not thread-safe) ,当多个线程同时使用的时候,需要程序员手动同步;另外,该容器 不允许放入null元素。
在这里插入图片描述

从图中我们看到,head指向首端第一个有效元素,tail指向尾端第一个可以插入元素的空位。因为是循环数组,所以head不一定总等于0,tail也不一定总是比head大。

2. BlockingQueue 接口

public class Solution1 {
    public static void main(String[] args) {
    	//创建延迟队列
    	DelayQueue<MyTask> queue=new DelayQueue<>();
    	
    	//创建任务丢到队列中
    	
    	//获取队列中的任务,这里只会跟超时时间最小的有关,和入队顺序无关    	
    }        
}
class MyTask implements Delayed{
	private long delayTime;//该任务需要再队列中的延迟的时候
    private long expire;//这个时间表示当前时间和延迟时间相加,这里就叫做到期时间
    private String taskName;//任务的名称
    
    public MyTask(long delayTime, String taskName) {
        this.delayTime = delayTime;
        this.taskName = taskName;
        this.expire = System.currentTimeMillis()+delayTime;
    }

	@Override
	public int compareTo(Delayed o) {
		return (int)(this.getDelay(TimeUnit.MILLISECONDS)-o.getDelay(TimeUnit.MILLISECONDS));
	}

	@Override
	public long getDelay(TimeUnit unit) {
		return unit.convert(this.expire-System.currentTimeMillis(), TimeUnit.MILLISECONDS);
	}
	
}


四、Set

1、Set接口常用功能

Set:不包含重复元素的集合,不保证顺序。而且方法和Collection一致。Set集合取出元素的方式只有一种:迭代器。
Set集合有多个子类,这里我们介绍其中的HashSet、TreeSet和LinkedHashSet这三个集合。
HashSet:哈希表结构,不同步,保证元素唯一性的方式依赖于
hashCode(),equals()方法。查询速度快。
Set方法完全来自Collection接口。

1.1哈希表

什么是哈希表呢?

哈希表底层使用的也是数组机制,数组中也存放对象,而这些对象往数组中存放时的位置比较特殊,当需要把这些对象给数组中存放时,那么会根据这些对象的特有数据结合相应的算法,算法这个对象在数组中的位置,然后把这个对象存放在数组中。而这样的数组就称为哈希数组,即就是哈希表。

当给哈希表中存放元素时,需要根据元素的特有数据结合相应的算法,这个算法其实就是Object中 的hashCode方法。由于任何对象都是Object类的子类,所以任何对象有拥有这个方法。即就是在给哈希表中存放对象时,会调用对象的hashCode方法,算出对象在表中的存放位置,这里需要注意,如果两个对象hashCode方法算出结果一样,这样现象称为哈希冲突,这时会调用对象的equals方法,比较这两个对象是不是同一个对象,如果equals方法返回的是true,那么就不会把第二个对象存放在哈希表中,如果返回的是false,就会把这个值存放在哈希表中。

总结:保证元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。

2 Set实现子类

2.1HashSet

此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。 是一个存放链表的数组。
此类为基本操作提供了稳定性能,这些基本操作包括 add 、 remove 、 contains 和 size ,假定哈希函数将这些元素正确地分布在桶中。对此 set 进行迭代所需的时间与 HashSet 实例的大小(元素的数量)和底层 HashMap 实例(桶的数量)的“容量”的和成比例。因此,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)

2.2LinkedHashSet

具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。

此类提供所有可选的 Set 操作,并且允许 null 元素。与 HashSet 一样,它可以为基本操作( add 、 contains 和 remove )提供稳定的性能,假定哈希函数将元素正确地分布到存储段中。由于增加了维护链接列表的开支,其性能很可能会比HashSet 稍逊一筹,不过,这一点例外: LinkedHashSet 迭代所需时间与 set 的大小 成正比,而与容量无关。 HashSet 迭代很可能支出较大,因为它所需迭代时间与其容量 成正比。

2.3TreeSet

底层数据结构是红黑树。(唯一,有序)

1.如何保证元素排序的呢?
自然排序
比较器排序
2.如何保证元素唯一性的呢?
根据比较的返回值是否是0来决定
TreeSet 底层数据结构采用红黑树来实现,元素唯一且已经排好序;唯一性同样需要重写hashCode和equals()方法,二叉树结构保证了元素的有序性。根据构造方法不同,分为自然排序(无参构造)和比较器排序(有参构造),自然排序要求元素必须实现Compareable接口,并重写里面的compareTo()方法,元素通过比较返回的int值来判断排序序列,返回0说明两个对象相同,不需要存储;比较器排需要在TreeSet初始化是时候传入一个实现Comparator接口的比较器对象,或者采用匿名内部类的方式new一个Comparator对象,重写里面的compare()方法;

二叉查找树
要想了解二叉查找树,我们首先看下二叉查找树有哪些特性呢?
1, 左子树上所有的节点的值均小于或等于他的根节点的值
2, 右子数上所有的节点的值均大于或等于他的根节点的值
3, 左右子树也一定分别为二叉排序树
在这里插入图片描述

红黑树
红黑树就是一种平衡的二叉查找树,说他平衡的意思是他不会变成“瘸子”,左腿特别长或者右腿特别长。除了符合二叉查找树的特性之外,还具体下列的特性:
1.节点是红色或者黑色
2.根节点是黑色
3.每个叶子的节点都是黑色的空节点(NULL)
4.每个红色节点的两个子节点都是黑色的。
5.从任意节点到其每个叶子的所有路径都包含相同的黑色节点。
在这里插入图片描述

2.4TreeSet的两种排序方式比较

1.基本数据类型默认按升序排序
2.自定义排序
(1)自然排序:重写Comparable接口中的Compareto方法
(2)比较器排序:重写Comparator接口中的Compare方法

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {

        /**
         * 自定义规则的TreeSet
         * 客户端排序:自己写一个比较器,转给TreeSet
         *
         * 比较规则
         * 当TreeSet集合添加数据的时候就会触发比较器的compare()方法
         */
        Comparator<Integer> comp = new Comparator<Integer>() {
            /**
             * o1 当前添加的数据
             * o2 集合中已经存在的数据
             * 0: 表示 o1 == o2
             * -1 : o1 < o2
             * 1 : o1 > o2
             */
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println(o1+"--"+o2);
                return o2 -o1; //输出53 33 10,降序排序
              //  return  0;  //只输出一个元素:33
              //   return -1; //输出53 10 33,倒序输出
              //  return 1;  //输出33 10 55
            }
        };

        Set<Integer> s2 = new TreeSet<>(comp);
        s2.add(33);
        s2.add(10);
        s2.add(55);

        System.out.println(s2); //输入53 33 10,降序排序

    }
}

import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

/**
 * 使用TreeSet和Comparator(使用匿名类),写Test.java
 * 要求:对TreeSet中的元素
 *     1,2,3,4,5,6,7,8,9,10进行排列,
 * 排序逻辑为奇数在前偶数在后,
 * 奇数按照升序排列,偶数按照降序排列
 * 输出结果:1 3 5 7 9 10 8 6 4 2
 */
public class Test {
    public static void main(String[] args) {
        Set<Integer> s = new TreeSet<>(new Comparator<Integer>() {
            //重写compare方法
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println("o1="+o1+" o2="+o2);
                if(o2%2==0){
                    if (o1%2==0){
                            return o2 -o1;
                    }else{
                        return -1;
                    }
                }else {
                    if (o1%2==0){
                        return 1;
                    }else{
                        return o1 -o2;
                    }
                }


            }
        });

        s.add(2);
        s.add(6);
        s.add(4);
        s.add(1);
        s.add(3);
        s.add(5);
        s.add(8);
        s.add(10);
        s.add(9);
        s.add(7);

        Iterator iterator = s.iterator();

        while(iterator.hasNext()){
            System.out.print(iterator.next()+" ");
        }

    }
}

(1)List,Set都是继承自Collection接口,Map则不是
(2)List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。)
(3)Set和List对比:
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
(4)ArrayList与LinkedList的区别和适用场景
Arraylist:
优点:ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
缺点:因为地址连续, ArrayList要移动数据,所以插入和删除操作效率比较低。
LinkedList:
优点:LinkedList基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址,对于新增和删除操作add和remove,LinedList比较占优势。LinkedList 适用于要头尾操作或插入指定位置的场景
缺点:因为LinkedList要移动指针,所以查询操作性能比较低。
适用场景分析:
当需要对数据进行对此访问的情况下选用ArrayList,当需要对数据进行多次增加删除修改时采用LinkedList

五、Collections集合工具

Collections工具类使用
在集合框架中给我们提供相应的工具类,这个工具类存放在java.util包中,类名是Collections,它是专门用来操作集合的工具类,并且其中的方法都是静态的,不需要创建对象就可以使用。

  1. 获取Collection最值。
    public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) 获取集合中最大元素(按照默认比较方式)
    public static T max(Collection<? extends T> coll,Comparator<? super T> comp)获取集合中最大元素(按照指定比较器比较方式)
    public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll)获取集合中最小元素(按照默认比较方式)
    public static T min(Collection<? extends T> coll,Comparator<? super T> comp)获取集合中最小元素(按照指定比较器比较方式)
  1. 对List集合排序,也可以二分查找。
    public static <T extends Comparable<? super T>> void sort(List list)对集合排序(按照默认比较方式)
    public static void sort(List list,Comparator<? super T> c)对集合排序(按照指定比较器比较方式)
    public static int binarySearch(List<? extends Comparable<? super T>> list,T key)二分法查找
  1. 对排序逆序。
    public static Comparator reverseOrder() 对集合原有排序强行逆转
  2. 可以将非同步的集合转成同步的集合。
    public static List synchronizedList(List list)将非同步List集合转成同步List集合
    5.获取任意集合的枚举对象
    public static Enumeration enumeration(Collection c)

六、 映射

1.Map接口常用功能

1.void clear() :从此映射中移除所有映射关系(可选操作)。
2.boolean containsKey(Object key) :如果此映射包含指定键的映射关系,则返回 true 。 boolean containsValue(Object value) :如果此映射将一个或多个键映射到指定值,则返回true 。
3.Set<Map.Entry<K,V>> entrySet() :返回此映射中包含的映射关系的 Set 视图。
4.boolean equals(Object o) :比较指定的对象与此映射是否相等。
5.V get(Object key) :返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回null 。
6.int hashCode() :返回此映射的哈希码值。
7.boolean isEmpty() :如果此映射未包含键-值映射关系,则返回 true 。 Set 8.keySet() :返回此映射中包含的键的 Set 视图。
9.V put(K key, V value) :将指定的值与此映射中的指定键关联(可选操作)。
10.void putAll(Map<? extends K,? extends V> m) : 从指定映射中将所有映射关系复制到此映射中(可选操作)。
11.V remove(Object key) :如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
12.int size() :返回此映射中的键-值映射关系数。
13.Collection values() :返回此映射中包含的值的 Collection 视图。

2.Map实现子类

2.1HashTable

此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。
为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。

Hashtable 的实例有两个参数影响其性能:初始容量 和加载因子 。容量 是哈希表中桶 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为 open :在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。加载因子 是对哈希表在其容量自动增加之前可以达到多满的一个尺度。初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。

通常,默认加载因子(0.75)在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查找某个条目的时间(在大多数 Hashtable 操作中,包括 get 和 put 操作,都反映了这一点)。

初始容量主要控制空间消耗与执行 rehash 操作所需要的时间损耗之间的平衡。如果初始容量大于 Hashtable 所包含的最大条目数除以加载因子,则永远 不会发生 rehash 操作。但是,将初始容量设置太高可能会浪费空间。
注意,此实现是同步的。

2.2Properties

Hashtable 的子类 Properties 类表示了一个持久的属性集。 Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
一个属性列表可包含另一个属性列表作为它的“默认值”;如果未能在原有的属性列表中搜索到属性键,则搜索第二个属性列表。

2.3HashMap

基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和null 键。(除了非同步和允许使用 null 之外, HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
注意,此实现不是同步的。

2.4LinkedHashMap

Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序 )。
注意,此实现不是同步的。

2.5TreeMap

TreeMap基于红黑树数据结构的实现,键值可以使用Comparable或Comparator接口来排序。TreeMap继承自AbstractMap,同时实现了接口NavigableMap,而接口NavigableMap则继承自SortedMap。SortedMap是Map的子接口,使用它可以确保图中的条目是排好序的。

在实际使用中,如果更新图时不需要保持图中元素的顺序,就使用HashMap,如果需要保持图中元素的插入顺序或者访问顺序,就使用LinkedHashMap,如果需要使图按照键值排序,就使用TreeMap。

在这里插入图片描述
遍历map实例

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class Test {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("first", "linlin");
        map.put("second", "好好学java");
        map.put("third", "sihai");
        map.put("first", "sihai2");


        // 第一种:通过Map.keySet遍历key和value
        System.out.println("===================通过Map.keySet遍历key和value:===================");
        for (String key : map.keySet()) {
            System.out.println("key= " + key + "  and  value= " + map.get(key));
        }

        // 第二种:通过Map.entrySet使用iterator遍历key和value
        System.out.println("===================通过Map.entrySet使用iterator遍历key和value:===================");
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            System.out.println("key= " + entry.getKey() + "  and  value= "
                    + entry.getValue());
        }

        // 第三种:通过Map.entrySet遍历key和value
        System.out.println("===================通过Map.entrySet遍历key和value:===================");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println("key= " + entry.getKey() + "  and  value= "
                    + entry.getValue());
        }

        // 第四种:通过Map.values()遍历所有的value,但是不能遍历键key
        System.out.println("===================通过Map.values()遍历所有的value:===================");
        for (String v : map.values()) {
            System.out.println("value= " + v);
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值