Java核心类库之集合及常见数据结构

引言:类集设置的目的
为了方便用户操作各个数据结构,所以引入了类集的概念,可以把类集称为java对数据结构的实现。
类中几个最大的操作接口:Collection(单值)、Map(双值)、Iterator(迭代器)。所有的类集操作的接口或类都在java.util包里。

一、链表与二叉树

1.链表

1)链表的定义

链表是由一组不必相连的内存结构(节点),按特定的顺序链接在一起的抽象数据类型(表示数学中抽象出来的一些操作的集合)。

2)链表的离散存储线性结构

链表的n个节点离散分配,彼此通过指针相连,每个节点只有一个前驱节点和一个后续节点,首节点没有前驱节点,尾节点没有后继节点。

3)链表的优点

空间没有限制,插入删除元素快。

4)链表缺点

存取速度慢

5)链表的分类

单链表

单链表由各个内存结构通过一个 Next 指针链接在一起组成,每一个内存结构都存在后继内存结构(链尾除外),内存结构由数据域 Data 和 Next 指针域组成。
在这里插入图片描述

class Node{
        Object data;
        Node next;
	}
双向链表

双链表由各个内存结构通过指针 Next 和指针 Prev 链接在一起组成,每一个内存结构都存在前驱内存结构和后继内存结构(链头没有前驱,链尾没有后继),内存结构由数据域、Prev 指针域和 Next 指针域组成。
在这里插入图片描述

class Node{
		Node prev;
        Object data;
        Node next;
	}
循环链表–循环单链表

循环单链表由各个内存结构通过一个指针 Next 链接在一起组成,每一个内存结构都存在后继内存结构,内存结构由数据域 Data 和 Next 指针域组成。
在这里插入图片描述

链表的尾节点的next指针指向首节点的Data
循环双链表

由各个内存结构通过指针 Next 和指针 Prev 链接在一起组成,每一个内存结构都存在前驱内存结构和后继内存结构,内存结构由数据域 Data 、Prev 指针域和 Next 指针域组成。
在这里插入图片描述

6)链表的核心操作

插入、删除、查找(遍历)

7)下面对于单链表作简单代码示例:
(1)节点:
import java.util.Objects;
/**
 * @author Mr Bai`s Soda
 */
public class Node1<T> {
    /**结点值*/
    T value;
    /**结点的next指针*/
    Node1 next;

    public Node1(T value) {
        this.value = value;
        next = null;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Node1)) {
            return false;
        }
        Node1<?> node1 = (Node1<?>) o;
        return Objects.equals(value, node1.value);
    }

    @Override
    public int hashCode() {
        return Objects.hash(value);
    }

    @Override
    public String toString() {
        return "Node1{" + "value=" + value + '}';
    }
}

(2)节点的操作类:
/**
 * @author Mr Bai`s Soda
 */
public class MyLinkedList<T> {
    /**定义头结点和表示结点数量的计数器*/
    private Node1<T> head;
    public static int size;
    /**初始化链表*/
    public MyLinkedList() {
        this.head = new Node1<T>(null);
    }
    /**增加结点*/
    public void add(T value) {
        /**申请新节点存储传入的值*/
        Node1<T> newNode1 = new Node1<>(value);
        /**申请临时指针指向头结点*/
        Node1<T> temp = head;
        /**如果首结点是空*/
        if (head.next == null) {
            head.next = newNode1;
            size++;
            return;
        }
        /**用temp遍历单链表,找到尾结点*/
        while (temp.next != null) {
            temp = temp.next;
        }
        /**将新结点挂载到尾结点,计数加1*/
        temp.next = newNode1;
        size++;
    }
    /**删除结点*/
    public void remove(T value) {
        /**申请新节点存储传入的值*/
        Node1<T> newNode1 = new Node1<>(value);
        /**申请临时指针指向头结点*/
        Node1<T> temp = head;
        /**找到与所要删除的结点值相同的上一个结点*/
        while (!temp.next.equals(newNode1) && temp.next != null) {
            temp = temp.next;
        }
        /**将要删除的结点的下一个结点挂到要删除的结点的上一个结点,计数减1*/
        if (temp.next.equals(newNode1)) {
            temp.next = temp.next.next;
            size--;
            System.out.println("删除成功!");
        } else {
            /**遍历结束,无此结点*/
            System.out.println("无此结点!");
        }
    }
    /**改变结点的值*/
    public void replace(int index,T value){
        /**申请新节点存储传入的值*/
        Node1<T> newNode1 = new Node1<>(value);
        /**申请临时指针指向头结点*/
        Node1<T> temp = head;
        /**申请临时计数变量,统计是第几个下标*/
        int count = 0;
        /**找到与要修改的下标的上一个结点跳出循环*/
        while (count < index-1 && temp.next != null) {
            temp = temp.next;
            count++;
        }
        /**将要修改的结点的下一个结点挂到新结点上,并将新结点挂到要修改的结点的上一个结点,由GC回收原结点*/
        if (count < index) {
            newNode1.next = temp.next.next;
            temp.next = newNode1;
            System.out.println("修改成功!");
        } else {
            /**遍历结束,无此结点*/
            System.out.println("无此结点!");
        }
    }
    /**查找指定值的结点*/
    public void find(T value) {
        /**申请新节点存储传入的值*/
        Node1<T> newNode1 = new Node1<>(value);
        /**申请临时指针指向头结点*/
        Node1<T> temp = head;
        int count = 0;
        /**找到要查找的值的上一个结点*/
        while (!temp.next.equals(newNode1) && temp.next != null) {
            temp = temp.next;
            count++;
        }
        /**打印所查询结点的下标,为count+1*/
        if (temp.next.equals(newNode1)) {
            System.out.println("查找成功,该结点下标为:"+(count+1));
        } else {
            System.out.println("无此结点!");
        }
    }
    /**遍历链表*/
    public void show() {
        Node1<T> temp = head;
        System.out.println("该链表一共存储了:"+size+"个结点");
        /**调用重写的toString方法,遍历输出所有结点*/
        while (temp.next != null) {
            System.out.println(temp.next.toString());
            temp = temp.next;
        }
    }
}
(3)测试类:
/**
 * @author Mr Bai`s Soda
 */
public class Main {
    public static void main(String[] args) {
        MyLinkedList<Integer> myLinkedList = new MyLinkedList<>();
        myLinkedList.add(8);
        myLinkedList.add(9);
        myLinkedList.add(10);
        myLinkedList.add(12);
        myLinkedList.show();
        System.out.println("-----------------------");
        myLinkedList.remove(10);
        myLinkedList.show();
        System.out.println("-----------------------");
        myLinkedList.replace(2,15);
        myLinkedList.show();
        myLinkedList.find(12);
    }
}

2.二叉树

1)二叉树及其格式

二叉树是树的一种,每个节点最多可以有两个子树,即结点的度最大为2.

	class Node{
        Object data;
        Node left;
        Node right;
	}

二叉树可分为斜树、满二叉树、完全二叉树等,在此不作详述。

2)二叉树的遍历

		先序遍历:根左右
		中序遍历:左根右
		后序遍历:左右根

3)二叉树一些性质

(1)二叉树第 i 层上的结点数目最多为 2^(i-1) (i≥1)
(2)深度为 h 的二叉树至多有 2^h-1 个结点(h≥1)
(3)包含 n 个结点的二叉树的高度至少为 log2 (n+1)
(4)在任意一棵二叉树中,若终端结点的个数为 n0,度为 2 的结点数为 n2,则 n0=n2+1

4)简单代码示例

(1)节点
import java.util.Objects;
/**
 * @author Mr Bai`s Soda
 */
public class Node2 {
    /**结点值*/
    int value;
    /**结点的左右孩子指针*/
    Node2 leftChild;
    Node2 rightChild;

    public Node2() {}
    /**构造函数*/
    public Node2(int value) {
        this.value = value;
        this.leftChild = null;
        this.rightChild = null;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Node2)) {
            return false;
        }
        Node2 node2 = (Node2) o;
        return value == node2.value;
    }

    @Override
    public int hashCode() {
        return Objects.hash(value);
    }
    /**toShow显示结点值*/
    public void toShow() {
        if (this != null) {
            System.out.println(value);
        }
    }
}
(2)节点操作类
/**
 * @author Mr Bai`s Soda
 */
public class MyBinarySearchTree<T> {
    /**定义二叉查找树的根结点和表示二叉树结点数量的计数器*/
    private Node2 root;
    public static int size;
    /**初始化二叉树*/
    public MyBinarySearchTree() {}
    /**返回根结点*/
    public Node2 getRoot() {
        return root;
    }
    /**增加结点*/
    public void add(int value) {
        /**申请新节点存储传入的值*/
        Node2 newNode2 = new Node2(value);
        /**如果根结点是空*/
        if (root == null) {
            root = newNode2;
            size++;
        } else {
            /**申请临时指针指向根结点,并定义一个双亲结点指针*/
            Node2 temp = root;
            Node2 parent = null;
            while (true) {
                /**值小于根结点,在左边插入*/
                if (value < temp.value) {
                    parent = temp;
                    temp = temp.leftChild;
                    /**当其左孩子为空,把新结点挂到左孩子结点上*/
                    if (temp == null) {
                        parent.leftChild = newNode2;
                        size++;
                        break;
                    }
                } else if (value > temp.value) {
                    /**值大于根结点,在右边插入*/
                    parent = temp;
                    temp = temp.rightChild;
                    /**当其右孩子为空,把新结点挂到右孩子结点上*/
                    if (temp == null) {
                        parent.rightChild = newNode2;
                        size++;
                        break;
                    }
                } else {
                    /**新插入的结点值与已插入某个结点值相同*/
                    System.out.println("不能插入值相同的结点!");
                    return;
                }
            }
        }
    }
    /**先序遍历*/
    public void preOder(Node2 node2) {
        if (node2 == null) {
            return;
        }
        node2.toShow();
        preOder(node2.leftChild);
        preOder(node2.rightChild);
    }
    /**中序遍历*/
    public void inOder(Node2 node2) {
        if (node2 == null) {
            return;
        }
        inOder(node2.leftChild);
        node2.toShow();
        inOder(node2.rightChild);
    }
    /**后序遍历*/
    public void postOder(Node2 node2) {
        if (node2 == null) {
            return;
        }
        postOder(node2.leftChild);
        postOder(node2.rightChild);
        node2.toShow();
    }
}
(3)测试类
/**
 * @author Mr Bai`s Soda
 */
public class Main {
    public static void main(String[] args) {
        MyBinarySearchTree myBinarySearchTree = new MyBinarySearchTree();
        myBinarySearchTree.add(15);
        myBinarySearchTree.add(9);
        myBinarySearchTree.add(21);
        myBinarySearchTree.add(14);
        myBinarySearchTree.add(18);
        myBinarySearchTree.add(19);
        myBinarySearchTree.add(13);
        myBinarySearchTree.add(15);
        System.out.println("该树共存储了:"+MyBinarySearchTree.size+"个结点");
        System.out.println("先序遍历:");
        myBinarySearchTree.preOder(myBinarySearchTree.getRoot());
        System.out.println("中序遍历:");
        myBinarySearchTree.inOder(myBinarySearchTree.getRoot());
        System.out.println("后序遍历:");
        myBinarySearchTree.postOder(myBinarySearchTree.getRoot());
    }
}

3.常见的数据结构

1)栈

(1)栈的定义

栈又称堆栈,是限定仅在表尾进行插入和删除操作的线性表。能够插入和删除的一端称为栈顶,另一端称为栈底。
栈的入口、出口都是该栈的顶端位置。
压栈是存元素,弹栈是取元素。

(2)遵循原则

栈遵循先进后出的原则。

2)队列

(1)队列定义

队列是一种特殊的线性表,运算受到限制,一端插入,另一端删除,队尾进,队头删。
队列的入口出口各占一侧。

(2)遵循原则

队列遵循先进先出的原则。

3)数组

数组在此不作叙述。

4)链表

链表见博客开始部分

5)二叉树

见上。

二、Collection接口(重点)

1.定义

Collection接口是在整个java类集中保存单值的最大操作接口,里面每次操作的时候都只能保存一个对象的数据。此接口定义在java.util包中。

2.接口定义

	public interface Collection<E> extends Iterable<E>

3.常用方法

	public boolean add(E e); //向集合中插入一个元素
	public Iterator<E> iterator(); //为Iterator接口实例化
	public int size(); //求出集合中的元素个数
	通常使用子接口List(允许重复)、Set(不允许重复)。

4.List接口

1)定义

	public interface List<E> extends Collection<E>

2)常用方法

	public E get(int index); //根据索引位置取出每一个元素
	public ListIterator<E> listIterator(); //返回ListIterator接口的实例
	public E remove(int intdex); //删除指定位置的内容,重载了母接口的remove方法,删除的时候把元素取出,方便使用后删除。
	public void add(int index,E element); //修改指定位置的内容
	public E set(int index,E element); //在指定位置处增加元素
	List接口常用的实现类:ArrayList(95%)、Vector(4%)、LinkedList(1%)

3)ArrayList

(1)ArrayList是List接口的子类,定义:

public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, Serializable
可以使用add()方法添加元素,和remove()方法删除元素,根据指定位置取的内容的方法,只有List接口才有定义,其他的任何接口都没有任何的定义。

(2)示例
import java.util.ArrayList;
import java.util.List;
/**
 * @author Mr Bai`s Soda
 */
public class MyArrayList {
    public static void main(String[] args) {
        List<String> myArrayList = new ArrayList<>();
        myArrayList.add("this");
        myArrayList.add("is");
        myArrayList.add("myArrayList");
        System.out.println(myArrayList);
        System.out.println("------------------");
        myArrayList.remove(2);
        myArrayList.remove("this");
        System.out.println(myArrayList.get(myArrayList.size()-1));
    }
}

4)Vector

(1)Vector也属于List接口的子类,定义:

public class Vector extends AbstractList implements List, RandomAccess, Cloneable, Serializable
使用add()方法添加元素,和remove()方法删除元素,size()方法获取集合的大小。

(2)示例
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
/**
 * @author Mr Bai`s Soda
 */
public class MyVector {
    public static void main(String[] args) {
        List<String> myVector = new Vector<>();
        myVector.add("this");
        myVector.add("is");
        myVector.add("myVector");
        System.out.println(myVector);
        System.out.println("------------------");
        myVector.remove(1);
        myVector.remove("this");
        System.out.println(myVector.get(myVector.size()-1));
    }
}

5)Vector类和ArrayList类的区别

ArrayList性能较高,是采用了异步处理,支持Iterator、ListIterator输出
Vector性能较低,是采用了同步处理,除了支持Iterator、ListIterator输出,还支持Enumeration输出

5.Set接口

Set接口也是Collection的子接口但是接口里面的内容是不允许重复的。此接口没有List接口中定义的get(int index)方法,所以无法使用循环进行输出。
此接口中有两个常用的子类:HashSet、TreeSet

1)HashSet

里面的内容是无序存放的,可以进行循环输出,因为在Collection接口中定义了将集合变为对象数组进行输出(toArray())。

示例:

import java.util.HashSet;
import java.util.Set;
/**
 * @author Mr Bai`s Soda
 */
public class MyHashSet {
    public static void main(String[] args) {
        Set<String> myHashSet = new HashSet<String>();
        myHashSet.add("this");
        myHashSet.add("is");
        myHashSet.add("is");
        myHashSet.add("myHashSet");
        String [] strings = myHashSet.toArray(new String[]{});
        for (String s: strings) {
            System.out.println(s);
        }
    }
}

2)TreeSet

TreeSet是属于排序的子类,但是增加之后可以为用户进行排序功能的实现。

要想判断两个对象是否相等,第一种判断两个对象的编码是否一致,这个方法需要通过hashCode()完成,因为每个对象有唯一的编码,如果还需要进一步验证对象中的每个属性是否相等,需要通过equals()完成。所以此时需要覆写Object 类中的hashCode()方法,此方法表示一个唯一的编码,一般是通过公式计算出来的。

关于TreeSet的排序实现,如果是集合中对象是自定义的或者说其他系统定义的类没有实现Comparable接口,则不能实现TreeSet的排序,会报类型转(转向Comparable接口)错误。要添加到 TreeSet 集合中的对象的类型必须实现了 Comparable 接口。不过TreeSet的集合因为借用了Comparable接口,同时可以去除重复值,而HashSet虽然是Set接口子类,但是对于没有复写Object的equals和hashCode方法的对象,即使加入了HashSet集合中也是不能去掉重复值的。

示例:

import java.util.Set;
import java.util.TreeSet;
/**
 * @author Mr Bai`s Soda
 */
public class MyTreeSet {
    public static void main(String[] args) {
        Set<Integer> myTreeSet = new TreeSet<Integer>();
        myTreeSet.add(5);
        myTreeSet.add(10);
        myTreeSet.add(2);
        myTreeSet.add(2);
        System.out.println(myTreeSet);
    }
}

6.集合的输出

Iterator迭代输出(90%)、ListIterator(5%)、Enumeration(1%)、foreach(4%)

1)Iterator

Iterator属于迭代输出,基本的操作原理:不断的判断是否有下一个元素,有的话,则直接输出。

接口定义:public interface Iterator<E>

要想使用此接口,则必须使用 Collection 接口,在Collection接口中有iterator()方法,可以用于Iterator接口进行实例化操作。

hasNext()是否有下一个元素
next()取出内容
remove()删除当前内容

Iterator中的操作指针是在第一条元素之上,当调用next()方法的时候,获取当前指针指向的值并向下移动,使用hasNext()可以检查序列中是否还有元素。

注意:在进行迭代输出的时候如果要想删除当前元素,则只能使用Iterator接口中的remove()方法,而不能使用集合中的remove()方法。否则将出现未知的错误。

示例:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
 * @author Mr Bai`s Soda
 */
public class MyIterator {
    public static void main(String[] args) {
        List<String> myArrayList = new ArrayList<>();
        myArrayList.add("this");
        myArrayList.add("is");
        myArrayList.add("myArrayList");
        Iterator<String> iterator = myArrayList.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

2)foreach

for (类型 变量名 : 集合名或数组名) {
	System.out.println(变量名) ;
}

三、Map接口

1.Map接口及其定义

Map集合用于操作一对对象,里面的所有内容都按照 key–>value的形式保存,也称为二元偶对象。

接口定义:public interface Map<K,V>
Set<Map.Entry<K,V>> entrySet();//将Map接口变为Set集合
V get(Object key);//根据key找到其对应的value
Set<K> keySet();//将全部的key变为Set集合
Collection<V> values();//将全部的value变为Collection集合
V put(K key,V value);//向集合中增加内容

Map本身是一个接口,所以一般会使用以下的几个子类:HashMap、TreeMap、Hashtable

用put()和get()方法增加和取出元素

2.HashMap

1)定义

	public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
	可以用:
	Set<Integer> set = map.keySet(); // 得到全部的key
	Collection<String> value = map.values(); // 得到全部的value

2)示例

import java.util.*;
/**
 * @author Mr Bai`s Soda
 */
public class MyHashMap {
    public static void main(String[] args) {
        Map<Integer,String> myHashMap = new HashMap<Integer,String>();
        myHashMap.put(1,"this");
        myHashMap.put(2,"is");
        myHashMap.put(2,"isn't");
        myHashMap.put(3,"myHashMap");
        String string = myHashMap.get(2);
        System.out.println(string);
        System.out.println("-------------------");
        Set<Integer> key = myHashMap.keySet();
        Collection<String> value = myHashMap.values();
        Iterator<Integer> iterator1 = key.iterator();
        Iterator<String> iterator2 = value.iterator();
        while (iterator1.hasNext()) {
            System.out.print(iterator1.next()+"、");
        }
        System.out.println();
        while (iterator2.hasNext()) {
            System.out.print(iterator2.next()+"、");
        }
    }
}

3.Hashtable

使用方法如:
Map<String, Integer> numbers = new Hashtable<String, Integer>();

Hashtable中不能向集合中插入null值。

示例:

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
/**
 * @author Mr Bai`s Soda
 */
public class MyHashTable {
    public static void main(String[] args) {
        Map<Integer,String> myHashtable = new Hashtable<>();
        myHashtable.put(1,"this");
        myHashtable.put(2,"is");
        myHashtable.put(3,"myHashtable");
        String string = myHashtable.get(3);
        if (string != null) {
            System.out.println(string);
        }
    }
}

4.HashMap与Hashtable的区别

	HashMap:异步处理,性能较高,允许设置为null
	Hastabl:同步处理,性能较低,不允许设置null,否则将出现空指向异常

5.TreeMap

TreeMap子类是允许key进行排序的操作子类,其本身在操作的时候将按照key进行排序,另外,key中的内容可以为任意的对象,但是要求对象所在的类必须实现Comparable接口。

示例:

import java.util.*;
/**
 * @author Mr Bai`s Soda
 */
public class MyTreeMap {
    public static void main(String[] args) {
        Map<Integer,String> myTreeMap = new TreeMap<Integer,String>();
        myTreeMap.put(1,"this");
        myTreeMap.put(2,"is");
        myTreeMap.put(3,"myTreeMap");
        Set<Integer> set = myTreeMap.keySet();
        Iterator<Integer> iterator = set.iterator();
        while (iterator.hasNext()) {
            int i = iterator.next();
            System.out.println(i+"-->"+myTreeMap.get(i));
        }
    }
}

6.Map的输出

Map接口非要使用Iterator进行输出的话,则可以按照如下的步骤进行:
1、使用Map接口中的entrySet()方法将Map接口的全部内容变为Set集合
2、可以使用 Set接口中定义的iterator()方法为Iterator接口进行实例化
3、之后使用Iterator接口进行迭代输出,每一次的迭代都可以取得一个Map.Entry的实例
4、通过Map.Entry进行key和value的分离

K getKey()//得到 key
V getValue()//得到 value
例如:
Set<Map.Entry<String, String>> set = map.entrySet();// 变为Set实例
Iterator<Map.Entry<String, String>> iter = set.iterator();
while (iter.hasNext()) {
	Map.Entry<String, String> me = iter.next();
	System.out.println(me.getKey() + " --> " + me.getValue());
}

四、Collections类

Collections实际上是一个集合的操作类

1.定义

public class Collections extends Object
List<String> all = Collections.emptyList() ;// 空的集合

使用Collections类返回的空的集合对象,本身是不支持任何的修改操作的,因为所有的方法都没实现。

List<String> all = new ArrayList<String>();
Collections.addAll(all, "A", "B", "C");// 向集合增加元素

2.在 java 的集合中,判断两个对象是否相等的规则

1)判断两个对象的 hashCode 是否相等

	如果不相等,认为两个对象也不相等,完毕
	如果相等,转入 2

2)判断两个对象用 equals 运算是否相等

	如果不相等,认为两个对象也不相等
	如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)

五、总结

1、 类集就是一个动态的对象数组,可以向集合中加入任意多的内容。
2、 List接口中是允许有重复元素的,Set接口中是不允许有重复元素。
3、 所有的重复元素依靠hashCode()和equals进行区分
4、 List接口的常用子类:ArrayList、Vector
5、 Set接口的常用子类:HashSet、TreeSet
6、 TreeSet是可以排序,一个类的对象依靠Comparable接口排序
7、 Map接口中允许存放一对内容,key–>value
8、 Map接口的子类:HashMap、Hashtable、TreeMap
9、 Map使用Iterator 输出的详细步骤

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白的Soda

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值