JavaSE---集合

集合

框架图

在这里插入图片描述

泛型

为什么会有泛型呢?
早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。

参数化类型,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可

以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

puimport java.util.ArrayList;
/*
  泛型:类型参数化,参数化类型,将类型作为参数传入
   <任意的单个大写字母,可以有多个>
   泛型的传入的类型只能是类类型
  如果没有写,默认是Object
 */
public class TypeDemo<T,E>{
    T name;
    E color;
    public T  test(E e){
        return null;//引用类型的默认值可以是null
    }
    TypeDemo<String,Integer> t = new TypeDemo<String,Integer>();
    String  s =  t.test(1);


    
    
    public static void main(String[] args) {
        //没有定义类型,默认是Object,存比较方便
        ArrayList alist = new ArrayList();
        alist.add("a");
        alist.add(1);
        alist.add(true);

        for(int i=0;i<alist.size();i++){
            Object obj =   alist.get(i);
            if(obj instanceof String){//在具体的使用时,需要向下转型,类型不确定
                String s = (String) obj;
            }
            if(obj instanceof Integer){
                Integer s1 = (Integer) obj;
            }
            if(obj instanceof Boolean){
                Boolean s2 = (Boolean) obj;
            }
        }
    }
}

1、泛型的类型参数只能是类类型(包括自定义类)

2、泛型的类型参数可以有多个。

如果没有定义具体类型,默认为Object

集合概念

数组用来保存一组类型相同(基本类型或引用类型)的元素,一旦定义,长度不能再改变

集合:能够动态增长长度的容器

1.集合类的长度可变.

2.不同的数据存储,操作方式不同. 底层有数组,链表,树,哈希表实现.

3.集合只能是引用类型

集合体系

Java的集合框架是由很多接口、抽象类、具体类组成的,都位于java.util包中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UE8o8JCi-1612264565180)(C:\Users\25919\AppData\Roaming\Typora\typora-user-images\1611901653028.png)]

Collection 单列集合

List: 元素可重复

​ ArrayList: 底层是数组实现 LinkedList:底层是链表实现 Vector:底层也是数组实现,线程安全

Set:不可重复

​ HashSet:底层hash表实现 TreeSet:底层是红黑树实现

Map 双列集合

​ HashMap TreeMap Hashtable

迭代器

Collections类

Collection接口

Collection 接口-定义了存取一组对象的方法,其子接口Set和List分别定义了存储方式。

​ Set 中的数据对象没有顺序且不可以重复。

​ List 中的数据对象有顺序且可以重复。

在Collection 中定义的方法是集合中共有的基本方法:

 public static void main(String[] args) {
        /*
        集合中默认可以存储任意数据类型,建议使用泛型,存储同一类型的数据
         */
        Collection<String> c = new ArrayList<String>();//默认Object类
        c.add("a");
        c.add("b");
        c.add("c");
        c.add("d");
       // c.clear();//清空
        c.remove("a");//删除指定元素,有则返回true,没有返回false
        c.contains("a");//判断是否包含某元素
        c.isEmpty();//是否为空
     
        c.removeIf(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.equals("a");//删除的过滤条件
            }
        });
        //删除满足给定谓词的此集合的所有元素。
     
     
        c.stream().forEach((a)->System.out.println(a));//返回以此集合作为源的顺序 Stream 。
        //流的方式遍历集合
     
      
        test(1,2,3,4);
    }

    //int...a可变长度的参数,
    // 本质是一个数组,
    // 一个参数列表只能由一个可变长度的参数,
    // 并且只能放在末尾
    public static void test(int a,int...b){
        System.out.println(a);//1
        System.out.println(b);//[2,3,4]
    }

集合与数组的转换

		//集合转数组
        Object[] obj = c.toArray();//默认object
        String[] s = c.toArray(new String[c.size()]);//用给定的类型转数组
 //数组转集合
        int[] a = {1, 2, 3, 4};
        int[] b = {1, 2, 3, 4};
        Collection c1 = Arrays.asList(a,b);
        //Collection c2 = Arrays.asList(1,2,3,4);
        //System.out.println(c2.remove(2));//不支持的操作2表示索引
        List c2 = Arrays.asList(1,2,3,4);//返回List类型,本质是Arrays$ArrayList(Arrays类中的一个内部类ArrayList)
        ArrayList alist =  new ArrayList<>(c2);//将List类型的集合,通过ArrayList构造方法转为集合

        System.out.println(alist.remove(2));//第二个元素3
        System.out.println(alist);//[1,2,4]

        System.out.println(c1);

可变长度参数

public static void main(String[] args){
test(1,2,3,4);
    }

    //int...a可变长度的参数,
    // 本质是一个数组,
    // 一个参数列表只能由一个可变长度的参数,
    // 并且只能放在末尾
    public static void test(int a,int...b){
        System.out.println(a);//1
        System.out.println(b);//[2,3,4]
    }

1-List接口

List继承了Collection接口,可以存储重复元素,有三个实现的类

ArrayList: 数组列表,数据采用数组方式存储。

LinkedList:链表

Vector:数组列表,添加同步锁,线程安全的

ArrayList

实现了长度可变的数组,在内存中分配连续的空间。

遍历元素和随机访问元素的效率比较高

底层是数组实现,查询快,增,删慢

创建之初,在底层创建一个默认长度的数组,当数组内容添加满了之后,再继续添加时,会扩容一个新数组,为原来的1.5倍

缺点:扩容后,元素不能存满,空间浪费

构造方法

 /*
        调用默认无参的构造方法,创建ArrayList对象时,并没有实际的创建数组
        第一次添加元素时创建,默认长度为10
         */
        ArrayList<String> alist = new ArrayList<String>();
        alist.add("aaa");
 		alist.add("bbb");
        alist.add("ccc");
        alist.add("ddd");//[aaa,bbb,ccc,ddd,null,null,null,null,null,null]
 /*
        调用有参的构造方法,创建ArrayList对象时,创建一个指定长度的数组
        this.elementData = new Objiect[initialCapacity];
         */
 ArrayList<String> alist1= new ArrayList<String>(10);

添加元素

ArrayList<String> alist = new ArrayList<String>();
        alist.add("aaa");
        alist.add("bbb");
        alist.add("ccc");
        alist.add("ddd");//[aaa,bbb,ccc,ddd,null,null,null,null,null,null]
        alist.add(3,"A");//向指定位置添加
        /*private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        容量最大值为Integer最大值-8
         */
        //当add添加元素时,数组满了后,会扩容,为原来的1.5倍,返回一个新数组
        /*add()当数组中不足以放下元素时,调用grow()进行扩容
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
         */

获取元素

  alist.get(2);//获取指定位置元素
        /*get()
          public E get(int index) {
                rangeCheck(index);

             return elementData(index);
         }直接返回索引处的元素
         */

删除元素

  		alist.remove(3);//删除第三个元素,会把要删除的元素返回
        alist.remove("c");//根据内容删除//有就返回true没有就返回false
		public class MyArrayList extends ArrayList {
   			 public static void main(String[] args) {
      			  MyArrayList mlist = new MyArrayList();
       			  mlist.add("a");
        		  mlist.add("a");
        		  mlist.add("a");
       		      mlist.add("a");
                  mlist.add("a");
                  mlist.removeRange(0,3);//删除指定区间的元素,
           //removeRange()受保护的方法protect,只能在子类中访问
    }
}

两个集合之间的操作

  ArrayList<String> alist = new ArrayList<String>();
        alist.add("a");
        alist.add("b");
        alist.add("c");
        alist.add("d");
        alist.add("e");
        alist.add("a");
 ArrayList<String> alist1 = new ArrayList<String>();
        alist1.add("a");
        alist1.add("b");
        alist1.add("c");
        alist1.add("d");
        alist1.add("e");
        alist1.add("a");
        alist.addAll(alist1);//把一个集合里的元素全部加到一个集合
        alist.containsAll(alist1);//判断一个集合是否包含另一个集合里的元素
        alist.removeAll(alist1);//如果有就删除相同的元素,返回true,没有就返回false
        alist.retainAll(alist1);//保留两个集合中相同的元素,有变化返回true

补充方法

   ArrayList<String> alist = new ArrayList<String>();
        alist.add("a");
        alist.add("b");
        alist.add("c");
        alist.add("d");
        alist.add("e");
        alist.add("a");//可以有重复元素
        //排序
        alist.sort(new Comparator<String>() {
            //使用匿名内部类,定义排序规则
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        alist.forEach((a)->System.out.println(a));//以流的形式遍历
        System.out.println(alist);
  		alist.set(2,"C");//替换指定位置的元素
        alist.indexOf("a");//从前往后找指定元素第一次出现的索引
        alist.lastIndexOf("d");//从后往前找第一次出现的元素的索引
        alist.subList(2,5);//截取指定区间的元素,返回一个新的集合

集合迭代

1-以流的方式遍历

list.stream().forEach((a)->System.out.println(a));

2-for循环遍历

//for循环遍历,可以从集合中删除元素,删除后下标与集合中的内容位置就对不上了
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
            if(list.get(i).equals("d")){
                list.remove(i);//删除某个元素
            }
        }

3-增强for循环实现遍历

  //增强for循环实现遍历,理论上不允许删除集合中的数据,
        // 如果需要可以删除一次并立即终止循环
        for (String temp:list) {//ConcurrentModificationException
            if(temp.equals("d")){
                list.remove(temp);
               // break;//可以删掉一个后终止循环
            }
        }

4-迭代器遍历-iterator()

  /*1.
            hasNext(),判断集合中还有没有元素,有就返回true,没有就false
            remove();删除元素,删除后里面的指针(记录遍历的元素序号)会向回退一下
         */
        Iterator<String> i =list.iterator();
        System.out.println(i);//java.util.ArrayList$Itr@1540e19d
        while (i.hasNext()){//返回true就进入循环
            String e = i.next();//向下走一次,获得一个元素
            if(e.equals("e")){
                i.remove();//删除元素e,必须使用迭代器中提供的remove
            }
        }
        System.out.println(list);//[a, a, b, c, d]

5-迭代器遍历-listIterator()

1

/*2.
           listIterator();只适用于list接口实现类
           遍历时还可以添加,设置,替换,移除,获取元素下标
         */
        ListIterator listit = list.listIterator();
        while (listit.hasNext()){
            System.out.println(listit.next()+";"+listit.nextIndex());
            listit.next();
            listit.add("a");
            listit.set("a");
            listit.remove();
        }
        System.out.println(list);

2从后往前输出元素

 /*
            listIterator(list.size())
            hasPrevious()有没有上一个元素
            previous()//获得上一个元素
             */

        ListIterator listit = list.listIterator(list.size());
        while (listit.hasPrevious()){
            System.out.println(listit.previous());//从后往前输出元素
        }
        System.out.println(list);

LinkedList

采用链表存储方式。插入、删除元素时效率比较高

底层是链表实现,查询慢,增删快

元素查找时从第一个节点向后查找

增加,删除元素时其他位置元素不动,只需要改变指针域的值即可

在这里插入图片描述

获取元素

 llist.get(2);//返回第2个元素,2不同于数组的索引,只代表元素序号
        /*LinkedList查找某个元素,从头结点或者为节点开始查找,查询效率低
         Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
         */

添加元素

 LinkedList<String> llist = new LinkedList<String>();
        llist.add("a");
        llist.add("b");
        llist.add("c");
        llist.add("d");
        llist.add("e");

补充

  public static void main(String[] args) {
        LinkedList<String> llist = new LinkedList<String>();
        //LinkedList里面提供了许多关于链表头尾操作得方法,用于模拟队列结构,栈结构操作
        llist.addLast("a");
        llist.addLast("b");
        llist.addLast("c");
        llist.addLast("d");
        llist.addLast("e");//从队尾添加
        llist.clear();
        System.out.println(llist.pollFirst());//返回null
        System.out.println(llist.peekFirst());//返回null
        /*集合内没有元素返回null
        public E peekFirst() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
     }
         */
        System.out.println(llist.removeFirst());//删除队首元素,
        /*集合内没有元素会报错
        public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }
         */
        System.out.println(llist);
        llist.addFirst("1");//返回值void
        llist.offerFirst("1");//在队首插入指定元素,返回值Boolean类型
        llist.offerLast("w");//在末尾插入指定元素,返回值Boolean类型
        System.out.println(llist);


    }

2-set接口

Set接口继承了Collection接口。

Set中所存储的元素是不重复的,但是是无序的, Set中的元素是没有索引的

Set接口有两个实现类

1-HashSet

HashSet的底层是HashMap,HashSet只是使用了HashMap键的那一列,是从HashMap扩展出来的一格单列存储

的类

 // HashSet  无序存储(不是按照添加顺序排列)
HashSet<String> set = new HashSet();//底层是HashMap
        /*public HashSet(){
        map = new HashMap<>();
        }
         */

在这里插入图片描述

底层数据结构:首先是哈希表(数组) 数组类型时Node(数据域,指针域)

​ 数组+链表+红黑树

第一次添加元素时,会在底层创建一个长度为16的哈希表

putVal(hash(key),key,value,false,true);

将key值传递到一个hash()中

return(key == null)?0:(hash = key.hashCode())^(h>>>16);

用内容的hash值,计算此元素在hash表中的位置

hash表默认长度为16,当容量添加为长度的0.75时,会自动扩容为原来的2倍

(16*0.75 = 12 )当链表长度大于8时,链表转为红黑树

HashSet类中的元素不能重复,即彼此调用equals方法比较,都返回false。

  HashSet<String> set = new HashSet();//底层是HashMap

        set.add("a");
        set.add("a");
        set.add("b");
        set.add("w");
        set.add("x");
        set.add("s");
        System.out.println(set);//[a, b, s, w, x]
 

1.哈希结构的底层是如何存储数据

哈希表本质也是数组

2.HashSet是如何判断内容重复
1.每次添加内容时,会用内容的hash值来判断是否相同,但是hash值这种方法不安全,可能会出现内容不同,hash值相同(快)
2.当出现内容不同hash值相同(hash碰撞,hash冲突)时,使用equals方法比较,比较的是内容是否相同(安全)

双保险:保证效率,又保证了安全

哈希表依赖于哈希值存储

3.hash值怎么来:

调用hashCode()方法,

两种情况

1.类中已经重写hashCode(),例如String类, 根据内容来计算hash值

2.类中没有重写hashCode(),调用Object类中hashCode()

2-TreeSet

可以给Set集合中的元素进行指定方式的排序。存储的对象必须实现Comparable接口。

TreeSet底层数据结构是二叉树(红黑树是一种自平衡的二叉树)

 public static void main(String[] args) {
        /*
        Set不能存储重复元素
        TreeSet可以按照元素的自然顺序排序

        底层是红黑树
         */
        TreeSet<String> tset = new TreeSet<>();
        tset.add("b");
        tset.add("a");
        tset.add("d");
        tset.add("c");
        tset.add("c");
        System.out.println(tset);//[a, b, c, d]
    }

红黑树与二叉树

在这里插入图片描述

3-Set遍历

1.流的方式**

tset.stream().forEach((a)->System.out.println(a));

2.增强for循环

for(String s:tset){
            System.out.println(s);
        }

3.迭代器

 Iterator<String> it = tset.iterator();
        while (it.hasNext()){
            String e= it.next();
            System.out.println(e);
        }

Map接口

Map接口

将键映射到值的对象(key->value)

一个映射不能包含重复的键

每个键最多只能映射到一个值

Collection values()方法

 Collection<String> list = hm.values();
        System.out.println(list);//获得值那一列[n, a, b, c]

HashMap

HashMap中元素的key值不能重复,即彼此调用equals方法,返回为false。排列顺序是不固定的。

put(key,value),value可以重复,key不能重复

键值对存储

无序存储

允许加进来一个为null的键

 public static void main(String[] args) {
        HashMap<String,String> hm = new HashMap<>();
        /*put(key,value),value可以重复,key不能重复
          键值对存储
          无序存储
         */
        hm.put("a","a");
        hm.put("a","b");//如果有重复的键,后面的值会覆盖掉前面的值
        hm.put("b","b");
        hm.put("c","c");
        hm.put(null,"n");//允许加进来一个为null的键
        System.out.println(hm);//{null=n, a=b, b=b, c=c}
        hm.remove("a");//删除键及对应的值
        hm.clear();//清空
        hm.containsKey("c");//是否包含键
        hm.containsValue("c");//是否包含值
        hm.size();//3,map集合里面有几组键值对
        hm.get("a");//获得键的值

    }

TreeMap

适用于按自然顺序或自定义顺序遍历键(key)。

TreeMap根据key值排序,key值需要实现Comparable接口,

重写compareTo方法。TreeMap根据compareTo的逻辑,对key进行排序。

键是红黑树结构,可以保证键的排序和唯一性

键值对,键不能重复

可以根据键得自然顺序排序

指定的键的类型的类必须实现Comparable接口,排序使用

 public static void main(String[] args) {
        /*
        键值对,键不能重复
        可以根据键得自然顺序排序
        指定的键的类型的类必须实现Comparable接口,排序使用
         */
        TreeMap<Integer,String> tm = new TreeMap<>();
        tm.put(2,"a");
        tm.put(1,"b");
        tm.put(4,"c");
        tm.put(1,"d");
        tm.put(3,"a");
        System.out.println(tm);//{1=d, 2=a, 3=a, 4=c}
    }

HashTable

实现了同步。

无序存储

初始容量是11

线程安全的

不允许加为null的键

public static void main(String[] args) {
          /*
        无需存储
        初始容量是11
        线程安全的
         */
        Hashtable<String,String> ht = new Hashtable<>();
        ht.put("a","a");
        ht.put("a","b");
        ht.put("c","c");
        ht.put("d","d");
       // ht.put(null,"n");//不允许加为null的键
        System.out.println(ht);//{a=b, d=d, c=c}无序存储
    }

Map集合遍历

1-流的方式遍历

 hm.forEach((k,v)->System.out.println(k+":"+v));

2-keyset()
keyset()先获取到m集合中所有的键,存储到set集合中
再对set集合进行遍历,通过每一次拿到的key获得对应的值

Set<String> keyset =  hm.keySet();
        for (String key:keyset){
            System.out.println(key+":"+hm.get(key));
        }

3-entryset()

entryset()将hm集合中的键值对封装到每一个entry对象中

 Set<Entry<String,String>> entrySet =  hm.entrySet();
        //增强for
        for(Entry<String,String> entry:entrySet){
            System.out.println(entry.getKey()+":"+entry.getValue());
        }
        //迭代器
        Iterator<Entry<String,String>> it =entrySet.iterator();
        while (it.hasNext()){
            Entry<String,String> entry = it.next();
            System.out.println(entry.getKey()+":"+entry.getValue());
        }

Collections类

是集合类的工具类,与数组的工具类Arrays类似

定义了大量静态方法

同步集合对象的方法

对List排序的方法

添加

ArrayList<String> list = new ArrayList<>();
        list.add("x");
        Collections.addAll(list,"a","b","c");

排序

 Collections.sort(list);

二分查找

 System.out.println(Collections.binarySearch(list,"c"));

覆盖

ArrayList<String> list = new ArrayList<>();
        list.add("x");
        Collections.addAll(list,"a","b","c");
ArrayList<String> list2 = new ArrayList<>();
        list2.add("a");
        list2.add("f");
        list2.add("d");
        list2.add("n");
        Collections.copy(list,list2);//后面集合的内容把前面集合的内容覆盖掉,后面集合容量小于等于前面集合

其余常用方法

 List<Object> list1 = Collections.emptyList();
        //list1.add("a");
        /*public void add(int index, E element) {
               throw new UnsupportedOperationException();
            }
        不能给里面添加元素
         */
        Collections.fill(list,"K");//用K把里面所有的内容填充掉
        System.out.println(list);//[K, K, K, K]
        System.out.println(Collections.max(list));//最大值
        Collections.replaceAll(list,"a","X");
        Collections.reverse(list);//逆序输出
        Collections.swap(list,0,2);//交换两个位置的元素
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 全国v3.0版javase加强第一阶段-第2套卷主要是指在javase基础上进行加强学习和应用。在这套卷中,学习者需要掌握更高级的Java编程知识和技巧。 首先,学习者需要对Java语言有扎实的掌握。他们应该熟练掌握Java的基本语法、数据类型、流程控制、面向对象等基础知识。在此基础上,他们还需要深入了解Java的高级特性,如多态、继承、接口等,并能够灵活运用这些知识解决实际问题。 此外,学习者还需要学习和掌握Java的核心类库。他们应该了解Java集合框架、IO流、多线程等核心类库的使用方法和原理,并能够利用这些类库进行开发和优化。 在加强第一阶段的学习中,学习者还应该注重实践。他们应该通过编写各种类型的Java程序来加深对知识的理解和掌握。这些程序可以是简单的练习题,也可以是实际的应用开发项目。 通过学习全国v3.0版javase加强第一阶段-第2套卷,学习者能够进一步提升自己的Java编程能力。他们将能够更加熟练地运用Java语言和核心类库进行开发,并能够解决更复杂、更实际的编程问题。这将为他们今后的学习和工作打下坚实的基础。 ### 回答2: 全国V3.0) JavaSE加强第一阶段-第2套卷是一套用于加强JavaSE知识的教材。JavaSEJava平台标准版的缩写,是Java语言的基础,涵盖了Java语言的基本语法、面向对象编程、异常处理、多线程、集合框架等内容。 这套教材的目的是在第一阶段的基础上,进一步加强学生对JavaSE的理解和应用能力。它通过一系列的教学案例,让学生掌握更深入的编程技巧和方法。对于想要进一步提升Java编程能力的学生来说,这套教材是一个很好的选择。 教材中的内容包括Java中的异常处理、文件操作、网络编程、GUI界面设计等多个方面。 这些内容都是Java编程中非常重要的知识点,掌握了这些知识,学生可以更加灵活地运用Java语言来解决实际问题。 教材的设计符合教育教学规律,注重理论与实践相结合,通过实际的编程案例,帮助学生巩固理论知识,并且锻炼解决实际问题的能力。 总之,全国V3.0) JavaSE加强第一阶段-第2套卷是一套旨在加强学生对JavaSE知识理解和应用能力的教材。它涵盖了JavaSE的核心内容,通过实例教学帮助学生锻炼编程能力。对于想要进一步提升Java编程水平的学生来说,这套教材是一个非常实用和有效的学习工具。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值