黑马程序员Java学习日记(4)集合(一)

本文详细介绍了集合类的概念、特点及各种集合类的区别,包括List、Set等接口及其具体实现类如ArrayList、LinkedList、HashSet等,还讲解了泛型的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------

1.前言:

(1)为什么会出现集合类:

    面向对象对事物的体现都是以对象的形式体现的,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的的一种方式。

(2)数组和集合类的区别:

     数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能储对象。

(3)集合类的特点:

     1、集合只用于存储对象

     2、集合长度是可变的

     3、集合可以存储不同类型的对象。

 

2.Collection接口:

    1、List接口:可存放重复元素,元素存取是有序的

    2、Set接口:不可以存放重复元素,元素存取是无序的。

         add方法的参数类型是Object,以便于接受任意类型对象。

         集合中存储的都是对象的引用(地址)。

3.Collection的共性方法:

       添加 add()

       删除 remove()

       修改 clear()

       查询 iterator()

       判断 contains()

 4.List集合:

(1)List集合的子类:

       1ArrayList  

            底层的数据结构是数组结构,线程同步,默认的长度是10,查找速度快。

       2LinkedList

            底层的数据结构是链表结构,查找速度慢,因为只知道相邻的元素,增删速度快。

       3Vector

            底层的数据结构是数组结构,也是线程同步的,被ArrayList替代了。

(2)List集合中的方法:

      1、添加:add(index,element);    //往某个位置添加元素。

                      addAll(index,Collection);//往某个位置添加集合,添加一堆元素。

      2、删除:remove(index);   //根据位置删除元素。

      3、修改:set(index,element);  //修改某个位置上的元素。


      4、查询:get(index);       //获取位置上某个元素。

                      subList(from,to);  //根据位置获取子列表。

                      listIterator();     //迭代器。

                      int indexOf(obj);  //获取指定元素的位置。
                      ListIterator listIterator(); //列表迭代器。


(3)接口ListIterator

该接口只能通过List集合的listIterator方法获取List集合特有的迭代器。ListIteratorIterator的子接口。在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常,也就是并发异常。
    所以,在迭代时,只能用迭代器的方法操作元素,可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作,如果想要其他的操作如添加,修改等,就需要使用其子在迭代过程中,准备添加或者删除元素

5.ArraryList:

代码例子:

(1)

    import java.util.*;

   class ListDemo1

   {

       public static void main(String[] args)

       {

              method_1();

       }

       public static void method_1()

       {

            ArrayList a = new ArrayList();

            a.add("java02");

            a.add("java07");

            a.add("java03");

            a.add("java04");

            a.add("java06");

 

         a.add(1,"java08"); //从角标1的位置上添加"java08"

         a.remove(2);     //删除角标2位置上对应的元素。

         a.set(3,"java09");  //把角标位3对应的元素换成"java09"。    

         System.out.println(a.get(3)); //获取角标3位置上的元素。

         System.out.println(a.subList(2,4);//从角标位24获取子列表。

         System.out.println(a.indexOf("java03");  //获取"java03"这个元素对应的位置。

         Iterator it =a.iterator();  //通过迭代器取出元素。

         while(it.hasNext())     //普通的迭代器只具备:取出,判断,和删除的功能。

          {                  

             Object o =it.next(); //这里需要强转,因为不知道取什么类型的元素。                 

              if(s.equals("java03")) //判断取出的元素中是否有"java03"这个元素。                        

                   it.remove();    //如果"java03"这个元素存在就将其删除。

            }

      }

}

(2)如果换成列表迭代器:

 ListIterator<String> it =a.listIterator();

while(it.hasNext())

{

Object o =it.next();

if(s.equals("java03")) //判断取出元素当中是否存在"java55"这个元素。

it.set("java55"); //如果取出的元素中"java03"这个元素存在,就将其修                         

                        //改成"java55"

          else

it.add("java88");//如果"java55"这个元素不存在,就添加元素"java88"

}

 

(3)去除ArrayList中的重复元素:

 

  思路:定义一个功能,把ArrayList传给这个功能,然后这个功能返回元素唯一的ArrayList

  代码例子:

  import java.util.*;

  class ArrayListTest

  {

     public atatic void main(String[] args)

     {

        ArrayList al = new ArrayList(); //定义一个集合,存入几对相同的元素。

        al.add("java01");

        al.add("java02");

        al.add("java01");

        al.add("java02");

        al.add("java03");

        al = singleElement(al);  //接收返回来的容器。

 

        System.out.println(al); //打印结果为[java01,java02,java03]

     }

     public static ArrayList singleElement(ArrayList al)//定义一个功能,把集合传进

     {                                      //进来。

         ArrayList newAl = new ArrayList();   //定义一个临时容器。

         Iterator it = al.iterator();  //遍历集合中的元素。

         while(it.hasNext())

         {

           Object obj = it.next();

         if(!newAl.contains(obj))  //如果临时容器中不包含遍历到的元素,

            newAl.add(obj);     //那么就将这个元素添加进临时容器。

        }

          return newAl;      //返回一个临时容器。

     }

  }

(4)将自定义对象作为元素存到ArrayList集合中,并去除重复元素:

代码例子:

import java.util.*;

class Person         //建立一个描述人的类。

{

   private String name;

   private int age;

   Person(String name,int age)

   {

      this.name= name;

      this.age= age;

    }

    public void setName(String name)

    {

        this.name=name;

     }

     public String getName()

     {

         return name;

      }

      public void setAge(int age)

      {

          this.age=age;

       }

       public int getAge()

      {

          return age;

       }

       public boolean equals(Object obj) //需要复写equals方法,因为Object中的                        

       {                                                     //equals方法判断的是对象的地址值是否

                if(!(obj instanceof Person))  //相同,我们需要建立自己的判断的方法。

                        return false

                   Person p =(Person)obj;

                 return this.name.equals(p.name) && this.age== p.age;

         }  

}

class ArrayLisTest

{

     public static void main(String[] args)

     {

           ArrayList al = new ArrayList();

           al.add(new Person("lisi",30));

           al.add(new Person("zhagnsan",20));

           al.add(new Person("lisi",30));

           al.add(new Person("wangwu",25));

 

           al = singleElement(al);

           Iterator it = al.iterator();

           while(it.hasNext())    //遍历返回的集合中的元素。

            {

               Object obj = it.next();

               Person p =(Person)obj;

               System.out.println(p.getName()+"..."+p.getAge());

              }

        }

         public static ArrayList singleElement(ArrayList al)//定义一个功能去除重复元素。

         {                                      

                     ArrayList newAl =new ArrayList();

                     Iterator it = al.iterator();

                     while(it.hasNext())

                     {

                         Object obj = it.next();

                         Person p =(Person)obj;

                         if(!newAl.contains(p))

                         newAl.add(p);

                      }

                     return newAl;

            }

}

总结:List集合判断元素是否相同,依据的是元素的equals方法。

 

6.LinkedList

(1) LinkedList的特有方法:

1addFirst();   //总是从开头增加。

2addLast();   //总是从结尾增加。

3getFirst();   //总是获取第一个位置的元素,获取元素,但不删除元素。如果集合中没有元素,则抛出异常。

4getLast();   //总是获取最后一个位置的元素,获取元素,但不删除元素。如果集合中没有元素,则抛出异常。

5removeFirst();  //总是获取第一个位置的元素,获取元素,并且删除元素。如果集合中没有元素,则抛出异常。

6removeLast();  //总是获取最后一个位置的元素,获取元素,并且删除元素。如果集合中没有元素,则抛出异常。

 

代码例子:

import java.util.*;

class LinkedListDemo

{

   Public static void main(String[] args)

   {

      LinkedList  link = new LinkedList();

      link.addFirst("java01");

      link.addFirst("java02");

      link.addFirst("java03");

     System.out.println(link);//打印的结果为[java03,java02,java01]

                                          //因为总是添加头。

                                         //如果用addLast()来添加,那么结果是[java01,java02,java03]

                                         //因为总是添加最后一位。

 

         while(!link=isEmpty( ))

          {

              System.out.println(link.removeLast());//当集合不为空时,取一个删一个。

          }

          System.out.println(link.getFirst()); //打印结果为 java03

          System.out.println(link.getFirst()); //打印结果为 java03,因为总是取第一个。

      } 

}

 

(2)JDK1.6出现了替代方法:

    

1offerFirst(); 替代了 addFirst(); //总是从开头增加。

2offerLast(); 替代了 addLast(); //总是从结尾增加。

3peekFirst(); 替代了 getFirst(); //总是获取第一个位置的元素。

4peekLast(); 替代了 getLast();  //总是获取最后一个位置的元素。

5pollFirst();  替代了 removeFirst(); //总是获取第一个位置的元素。

6pollLast();  替代了 removeLast();//总是获取最后一个位置的元素。

 

(3)使用LinkedList 模拟一个队列数据结构:

Import java.util.*;

class DuiLie

    private  LinkedList link;

    DuiLie()

    {

       Link = new LinkedList();

    }

    public void myAdd()

    {

      link.addtFirst();

    }

    public Obiect myGet()

    {

     return link.getFirst(); 

     }

    public boolean isNull()

    {

     return link.isEmpty();

    }

}

class LinkedListTest

{

   public static void mian(String[] args)

   {

      DuiLie  d = new DuiLie();

      d.myAdd("java01");

      d.myAdd("java02");

      d.myAdd("java03");

      while(!d.isNull())

      {

         System.out.println(d.myGet());//打印结果为[java01,java02,java03],

      }                           //先进先出。

   }

}

为什么要集合和方法封装起来?

  直接用LinkedList是可以完成的,但是LinkedList只有自身的含义,叫做链表,如果我们想把它做成我们项目相关的一些容器,我们要命名一些特定的名称来使用,那么这个时候我们就将原有的结构封装到我们自己的描述当中,并对外提供一个方便我们识别的名称。

 

7.set集合:

(1)set集合的子类:

     1HashSet

          底层的数据结构是哈希表,线程是非同步的。

     2TreeSet

          底层的数据结构是二叉树。

(2)Set集合的方法:

   Set集合和collection的功能是一致的。

8.HashSet

 (1)HashSet保证元素的唯一性:

        1、是通过元素的两个方法,hashCodeequals来完成。
        2、如果元素的HashCode值相同,才会判断equals是否为true
         3、如果元素的hashcode值不同,不会调用equals

 

 总结:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode 

      equals方法。


练习:往hashSet集合中存入自定对象姓名和年龄相同为同一个人,重复元素。

代码例子:

import java.util.*;

class Person         //建立一个描述人的类。

{

     private String name;

     private int age;

     Person(String name,int age)

     {

             this.name= name;

             this.age= age;

     }

     public void setName(String name)

    {

        this.name=name;

     }

     public String getName()

     {

        return name;

      }

     public void setAge(int age)

     {

         this.age=age;

      }

      public int getAge()

     {

         return age;

      }

       public boolean equals(Object obj) //需要复写equals方法。                      

       {                            

             if(!(obj instanceof Person))         

               return false

               Person p =(Person)obj;

               return this.name.equals(p.name) && this.age== p.age;

        }  

       public int hashCode()      //复写hashcode方法。

       

            System.out.println(this.name+"....hashCode"); 

            return name.hashCode()+age*37; 

        

}

class ArrayLisTest

{

    public static void main(String[] args)

   {

          HashSet hs = new HashSet();    //定义一个HashSet 集合。

         //向这个集合中添加自定义元素 

        hs.add(new Person("a1",11)); 

         hs.add(new Person("a2",12)); 

        hs.add(new Person("a3",13)); 

       

          Iterator it = hs.iterator();

          while(it.hasNext())   

          {

             Object obj = it.next();

             Person p =(Person)obj;

            System.out.println(p.getName()+"..."+p.getAge());

           }

      }

   }

HashSet是如何保证元素的唯一性的?

    是通过元素的两个方法,hashcodeequals来完成的。如果元素的hashcode值相同,才会判断equals是否为true,  如果元素的hashcode值不同,不会调用equals

                                            

9.TreeSet

(1)TreeSet可以对Set集合进行排序,第一种方式,让元素具备比较性。

 

让元素自身具备比较性,元素要实现Comparable接口,复写compareTo方法,比较的是元素的自然顺序。

 

如果想用TreeSet集合删除元素,或者是判断元素是否包含,都得用到compareTo

方法,返回0相同,返回1或者-1就不相同。

 

代码例子:

import java.util.*;

class Student implements Comparable// 实现Comparable接口。

{

     private String name;

     private int age;

     Student(String name,int age)

     {

        this.name=name;

        this.age=age;

     }

     public void setName(String name)

     {

       this.name=name;

     }

     public String getName()

     {

       return name;

     }

     public void setAge(int age)

     {

        this.age=age;

     }

     public int getAge()

    {

       return age;

    }

    public int compareTo(Object obj)//复写compareTo方法。

    {                                                   //让元素具备比较性。

           if(!(obj instanceof Student))

                 throw new RuntimeException("不是学生对象");

            Student s= (Student)obj;

            if(this.age>s.age)

               return 1;

            if(this.age==s.age)     //当元素的年龄相同时,就按照姓名排。

               return this.name.compareTo(s.name);

            return -1;

        }

 }

 

class TreeSetDemo

{

     public static void main(String[] args) 

    {

       TreeSet tree =new TreeSet();

       tree.add(new Student("lisi02",22));

       tree.add(new Student("lisi007",20));

       tree.add(new Student("lisi09",19));

       tree.add(new Student("lisi01",40));

        Iterator t = tree.iterator();

        while(t.hasNext())

        {

              Student s=(Student)t.next();

              System.out.println(s.getName()+"....."+s.getAge());

         }

        System.out.println("Hello World!");

     }

}

打印结果为:

            lisi09",19

            lisi007",20

            lisi02",22

            lisi01",40

(2)排序第二种方式,让集合具备比较性。

当元素不具备比较性,或者具备的比较性不是所需要的,这时就需要让集合自身具备比较性。

练习:按照字符串长度排序

代码:

import java.util.*;

class TreeSetTest

{

    public static void main(String[] args) 

    {

        //往集合中传入实现Comparator的子类对象。

        TreeSet tree = new TreeSet(new StringLengthCompare());

            tree.add("abcd");

            tree.add("cc");

            tree.add("cba");

            tree.add("z");

            tree.add("hahaha");

            Iterator it = tree.iterator();

            while(it.hasNext())

            {

               String s =(String)it.next();

               System.out.println(s);

             }

        }

}

//实现Comparator接口。

class StringLengthCompare implements Comparator

{

        //复写compare方法。

        public int compare(Object obj1,Object obj2)

        {

                String s1=(String)obj1;

                String s2=(String)obj2;

                //比较元素的长度。

               int num =new Integer(s1.length()).compareTo(new Integer(s2.length()));

              if(num==0)

              //当集合中出现长度相同的元素,那么就需要比较元素的自然顺序。

              return s1.compareTo(s2);

                return num;

        }

}

打印结果为:

                z

            cc

            cba

            abcd

            hahaha

 

总结:二叉树也是以return 0 来判断元素是否相同。如果自然顺序和比较器这两种方式都存在,那么就以比较器为主。

 

10.泛型:

泛型:JDK1.5版本以后出现的新特性,用于解决安全问题,是一个安全机制。

(1)泛型出现前的例子:

import java.util.*;

class ArrayListTest1

{

    public static void main(String[] args)

    {

      ArrayList al = new ArrayList();

      //这里我们添加了3个字符串对象。

      al.add("abc01");

      al.add("abc02");

      al.add("abc03");

      // al.add(8);     //这里我们添加了一个Integer对象。

       Iterator it = al.iterator();

       while(it.hasNext())

       {               

           /*这里需要强转,我们知道存的是字符串类型的元素,但是迭代器不知道取的是什么类型的元素,所以需要强

       转。*/                

             Object obj =it.next();

             //等取到以后,我们再把取到的元素向下转型,转成我们存入的类型。

             String s =(String)obj;

             System.out.println(s);

         }

     }

}

发现:

(1)取的时候不知道取什么类型的元素,需要强制转换。

(2)如果我们在上面的代码中添加个al.add(8),然后编译的时候不会报错,运行的时候才会报错,因为在定义集合的时候并没有声明是什么类型的,所以有可能存成不同的类型的数据。

 

(2)泛型出现后的例子:

import java.util.*;

class ArrayListTest1

{

    public static void main(String[] args)

    {

       ArrayList<String> al = new ArrayList<String>();

       al.add("abc01");

       al.add("abc02");

       al.add("abc03");

       al.add(8);

       Iterator<String> it = al.iterator();

       while(it.hasNext())

       {

           //Object obj =it.next(); 

            String s =it.next();

            System.out.println(s);

         }

     }

}

发现:

 1、加上泛型以后,不用强制转换类型了,因为集合声明了类型,迭代器也声明

    了和集合相同的类型。

 2、当我们忘集合里面添加了一个元素al.add(8),这时候编译的时候就会报错,

因为集合已经声明了是String类型的,也就是只能添加String类型的元素,

添加了其它类型的元素就会报错。

 

(3)那么泛型的作用是什么呢?

 1、将运行时期出现的问题class cast Exception,转移至编译时期,方便程序员

    解决问题,让运行时期的问题减少,提高安全性。

   

 2、避免了强制转换的麻烦。

 

(4)泛型的格式:

  通过<> 来定义要操作的引用数据类型。

 

11.泛型类:

(1)定义工具类时思考:

  我们要操作一个对象,操作哪一个呢?不知道,什么类型也不确定,那么就定义一个参数,对方在使用工具类的时候,由他来指定要操作什么类型的对象。

代码例子:定义一个工具类。

(2)泛型前做法:

cass Tool

{

     private Object obj;

     public void setObject(Object obj)

     {

        this.obj =obj;

     } 

     public Object getObject()

     {

        return obj;

     }          

}

class GenericDemo

{

   public  static void main(String[] args)

   {

      Tool t = new Tool();

      t.setObject(new student());//传进来的是学生对象。

      worker w =(workert).getObject();//我们想接收的是worker

   }

}

通过上例发现:我们想通过这个工具类来描述工人,但是我们可能在传对象的时候写错,比如写成了学生,那么编译的时候不会报错,运行的时候就会报错。

(3)泛型后做法:

class Utils<QQ>  //泛型类。

{

    private QQ q;

    public void setObject(QQ q)

    {

       this.q = q;

    }

    public QQ getObject()

    {

      return q;

    }

}

class GenericDemo

{

    public static void main(String[] args)

    {

      Utils<worker> u = new Utils<worker>();

      u.setObject(new whorker);  //如果写成new student(),编译会报错。

      worker w = u.getObject();  //不需要强转了。

   }

}

通过上例发现:

加上泛型以后,如果写成了学生对象,那么在编译时期就会报错,而且不需要强制转换了。

 

(4)那么什么时候定义泛型类呢?

当一个类中要操作的引用数据类型不确定的时候,以前是定义Object类完成,现在是定义泛型来完成。

 

(5)注意:如果是基本数据类型不确定的时候不能使用泛型。

12.泛型方法:

  (1)泛型除了可以定义在类上,还可以定义在方法上。

  (2)泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了,所以为了让不同的方法可以操作部同类型,而且操作的类型不确定,那么可以将泛型定义在方法上。

  (3)泛型定义在方法上,只能在功能中有效。

 

代码例子:

class Demo 

{

    public<T> void show(T t)   //泛型定义在方法上,需要在语句中声明。

    {

        System.out.println("show:"+t);

    }

    public<Q> void print(Q q)  //泛型定义在方法上,与上一个方法没有关系。

    {

         System.out.println("print=:"+q);

     }

}

class GenericDemo

{

    public static void main(String[] args)

    {

        Demo d = new Demo();

        d.show("haha"); //往里面传什么类型的参数,就显示什么参数。

        d.print(4);  //往里面传什么类型的参数,就打印什么参数。

     }

}

 

代码例子:

既可以有泛型类,也可以有泛型方法,包括静态方法。

class Demo<T> 

{

     public void show(T t)

     {

        System.out.println("show:"+t);

      }

      public<Q> void print(Q q)

      {

         System.out.println("print=:"+q);

       }

       public static <Y>void method(Y y);//泛型定义在静态方法上。

       {

          System.out.println(y);

       }

}

class GenericDemo

{

     public static void main(String[] args)

     {

       /*因为类上定义了泛型,所以在创建对象的时候需要指明操作的类型。

        我们指定类型是String*/

        Demo<String> d = new Demo<String>();

          d.show("haha");  

        /*show方法类型与类对象类型相同,也就是只能接收String类型的数据,

        现在传了一个整数类型的数据,所以会报错。

        d.show(8); 

       //print方法与类上定义的泛型无关,传什么类型的参数,打印什么参数。

        d.print(4);

        Demo.method("zhanghao");//静态方法用类名调用。

    }

}

(4)注意:静态方法不可以访问类上定义的泛型,如果静态方法要操作的引用数

         据类型不确定,可以将泛型定义在方法上。

13.泛型定义在接口上:

当接口不知道操作什么类型的数据时,可以将泛型定义在接口上。

代码例子:

interface Inter<T> //泛型定义在接口上。

{

   void show(T t);

}

//一个类实现接口,用的时候传一个实际的参数。

class InterImpl implements Inter<String>

{

     public void show(String s)

     {

        System.out.println(s);

     }

class GenericDemo

{

    public static void main(String[] args)

    {

        nterImpl i = new nterImpl();

        i.show("hahah");

     }

}

 

如果说实现的时候也不知道操作什么,那么实现的时候也是泛型。

代码例子:

class InterImpl implements Inter<T>

{

     public void show(T t)

     {

       System.out.println(t);

     }

class GenericDemo

{

    public static void main(String[] args)

    {

       //在创建对象的时候指定泛型。

       nterImpl<String> i = new nterImpl<String>();

        i.show("hahah");

     }

}

14.泛型限定:

(1)格式:

 1<? extends E>:可以接收E类型和E的子类型。

 2<? super E>:可以接收E类型和E的父类型。

 3<?> :通配符,也可以理解为占位符。意思就是具备泛型,但是操作什么类

   型不知道,就占个位置,表示里面肯定有类型。

(2)练习:

代码例子:

import java.util.*;

class  GenericDemo2

{

    public static void main(String[] args) 

    {

       ArrayList<Person> al = new ArrayList<Person>();

       al.add(new Person("abc1"));

       al.add(new Person("abc2"));

       al.add(new Person("abc3"));

         method(al);

      ArrayList<Student> al1 = new ArrayList<Student>();

        al1.add(new Student("abcd1"));

        al1.add(new Student("abcd2"));

        al1.add(new Student("abcd3"));

         //method(al1); 

     }

      //我们给这个方法定义的是Person类型的,所以当传Student的时候会报错。

     public static void method(ArrayList<Person> al)

     {

         Iterator<Person> it= al.iterator();

         while(it.hasNext())

         {

             System.out.println(it.next().getName());

          }

      }

}

class Person

{

    private String name;

    Person(String name)

    {

          this.name=name;

     }

    public void setName(String name)

    {

        this.name=name;

    }

    public String getName()

    {

        return name;

     }

}

class Student extends Person

{

     Student(String name)

     {

        super(name);

      }

}

发现:

那么如果我们想让PersonStudent同时调用method方法,怎么办?

那么就需要泛型限定。例如:把method方法里的参数改成是

public static void method(ArrayList<? extends Person> al),也就是,能接收Person类型和Person的子类型。如果再类一个worker也可以用这个方法。

<?><T>的区别:

<?>是一个占位符,意思就是占一个位置,里面肯定有类型,但是不知道什么类型,它不需要在语句中声明。

<T>里面代表的是一个具体的类型,它需要在语句中声明,而且传进去什么类型就是什么类型。

 

------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值