java基础-chapter13(集合)

集合

集合的好处

  1. 可以动态保存任意多个对象
  2. 提供一系列方便操作对象的方法:add、remove、set、get等
  3. 使用集合添加,删除新元素的示意代码

集合主要分为两组:(单列集合,双列集合)

1. Collection 接口有两个重要的子接口:List 和 Set,它们实现的子类都是单列集合

List list = new ArrayList();
//add 添加单个元素
list.add("小明");
list.add(10); 
//相当于list.add(new Integer(10));
list.add(true);

2. Map 接口的实现子类是双列集合,存放 key-value

Map map = new HashMap();
map.put("N01","北京");
map.put("N02","上海");
map.put("N03","深圳");

Collection

Collection框架图

3bb20d3ba79440f8b9b7069e0c11f353.png

collection中的方法 

public static void main(String[] args) {
        List list = new ArrayList();

        //add 添加单个元素
        list.add("小明");
        list.add(10); //list.add(new Integer(10));
        list.add(true);
        System.out.println(list); //输出:[小明, 10, true]
 
        //remove 删除指定元素
        list.remove(0); //1.删除索引0,返回被删除的对象(小明)
        System.out.println(list); //[10, true]
        list.remove(true); //2.删除true(具体的值),则返回boolean值
        System.out.println(list); //[10]

        //contains:查找元素是否存在
        System.out.println(list.contains("小明")); //false
        System.out.println(list.contains(10));//true

        //size:获取元素个数
        System.out.println(list.size()); //1

        //isEmpty:判断是否为空
        System.out.println(list.isEmpty()); //false
        //clear:清空
        list.clear();
        System.out.println(list); // []

        //addAll:添加多个元素
        List list1 = new ArrayList();
        list1.add("花木兰");
        list1.add("孙尚香");
        list1.add("后羿");
        list.addAll(list1);
        System.out.println(list.equals(list1)); //true
        System.out.println(list); //[花木兰, 孙尚香, 后羿]

        //containsAll:查找多个元素是否存在
        System.out.println(list.containsAll(list1)); //true

        //removeAll:删除多个元素
        list.add("武则天");
        list.removeAll(list1);//删除了[花木兰, 孙尚香, 后羿]
        System.out.println(list);//[武则天]
    }

Collection接口遍历对象方式

  1. Iterator对象成为迭代器,主要用于遍历Collection 集合中的元素
  2. 所有实现了Collection接口的集合类都有一个iterator()方法,用于返回一个实现了Iterator接口的对象,即可以返回一个迭代器
  3. Iterator仅用于遍历集合,Iterator本身并不存放对象

示例代码:

public class IteratorDemo {
    public static void main(String[] args) {
        Collection col = new ArrayList();
        col.add(new Book("三国演义", "罗贯中", 49.9));
        col.add(new Book("红楼梦", "曹雪芹", 29.9));
        col.add(new Book("西游记", "吴承恩", 69.1));
        col.add(new Book("水浒传", "施耐庵", 99.8));
        /*
        1.遍历col集合
        2.得到 col 对应的迭代器
         */
        Iterator iterator = col.iterator();
        //3.使用while循环遍历
        while (iterator.hasNext()) {  //判断是否还有数据
        //返回下一个元素,类型是Object
            Object obj = iterator.next();
            System.out.println("obj="+obj);
        }
        //快捷键 itit 快捷生成 while

        /* 当退出while循环后,这时的iterator迭代器,指向最后的元素
           如果需要再次遍历,需要重置我们的迭代器
            iterator = col.iterator();
         */
        iterator = col.iterator();

    }
}
//创建一个Book类
public class Book{
        private String name;
        private String author;
        private double price;

        public Book(String name, String author, double price) {
            this.name = name;
            this.author = author;
            this.price = price;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAuthor() {
            return author;
        }

        public void setAuthor(String author) {
            this.author = author;
        }

        public double getPrice() {
            return price;
        }

        public void setPrice(double price) {
            this.price = price;
        }

        @Override
        public String toString() {
            return "Book{" +
                    "name='" + name + '\'' +
                    ", author='" + author + '\'' +
                    ", price=" + price +
                    '}';
        }
    }

 增强for循环

特点:增强for循环就是简化版的iterator,本质一样,只能用于遍历集合和数组,可以代替iterator迭代器

示例代码:

public class ForDemo {
    public static void main(String[] args) {
        @SuppressWarnings("all")
        Collection col = new ArrayList();
        col.add(new Book("三国演义", "罗贯中", 49.9));
        col.add(new Book("红楼梦", "曹雪芹", 29.9));
        col.add(new Book("西游记", "吴承恩", 69.1));
        col.add(new Book("水浒传", "施耐庵", 99.8));
        //使用增强for 在 Collection集合
        //增强for循环,底层仍然是迭代器
        //快捷方式 I
        for (Object book : col){
            System.out.println("book=" + book);
        }
        //增强for 也可以直接在数组使用
        int nums[] = {1,3,5,7,8,9};
        for (int i : nums){
            System.out.println(i);
        }
    }
}

List接口和常用方法

List接口基本介绍

 List接口是Collection接口的子接口

1.List集合类中元素有序(即添加顺序和取出顺序一致)且可重复

2.List集合中的每个元素都有其对应的顺序索引,即支持索引

//List集合类中元素有序,且可重复
List list = new ArrayList();
list.add("jack");
list.add("tom");
list.add("peter");
list.add("tom"); //可重复
System.out.println(list); //[jack, tom, peter, tom]
//List集合中的每个元素都有其对应的顺序索引,即支持索引 索引从0开始
System.out.println(list.get(1)); //tom

3.List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素

4.JDK API中List接口实现的类有:ArrayList、LinkedList和Vector

e4d6a3318ca24c7ba78044e855389b5c.png

List常用方法

public class ListMethod {
    public static void main(String[] args) {
        @SuppressWarnings("all")
        List list1 = new ArrayList();
        list1.add("雅典娜");
        list1.add("夏洛特");
        //void add(int index,Object ele):在index位置插入ele元素
        list1.add(1,"孙尚香"); //在index为 1 的位置插入 孙尚香
        System.out.println(list1); //[雅典娜, 孙尚香, 夏洛特]

        //boolean addAll(int index,Collection eles):从index位置开始将eles中所有的元素添加进来
        List list2 = new ArrayList();
        list2.add("狗哥");
        list2.add("墨菲特");
        list1.addAll(1,list2); //在 孙尚香 之前插入
        System.out.println(list1); //[雅典娜, 狗哥, 墨菲特, 孙尚香, 夏洛特]

        //Object get(int index):获取指定index位置的元素
        System.out.println(list1.get(2)); //墨菲特

        //int indexOf(Object obj):返回obj在集合中出现的首次位置
        List list3 = new ArrayList();
        list3.add('g');
        list3.add('c');
        list3.add('a');
        list3.add('c');
        //获取字符 c 在 list3 中出现的首次位置
        int index = list3.indexOf('c');
        System.out.println(index); // 1

        //int lastIndexOf(Object obj):返回obj在集合中出现的最后位置
        System.out.println(list3.lastIndexOf('c')); //3

        //Object remove(int index):移除指定index位置的元素,并返回此元素
        System.out.println(list3.remove(0)); // g
        System.out.println(list3); //[c, a, c]

        //Object set(int index,Object ele):设置指定index位置的元素为ele,相当于是替换
        list3.set(1,'c'); //将list3中索引为1的元素替换成'c'
        System.out.println(list3); //[c, c, c]

        //List subList(int fromIndex, int toIndex):返回从fromIndex(包含)到toIndex(不包含)位置的子集合
        List list4 = new ArrayList();
        list4.add(1);
        list4.add(2);
        list4.add(3);
        list4.add(4);
        list4.add(5);
        List list5 = list4.subList(1,3);
        System.out.println(list5); //返回从索引 1 到索引 3 位置的子集合 不包含 3
        //输出 [2, 3]

    }
}

测试题 

使用List的实现类添加图书,并遍历,要求按价格低到高排序

示例代码

public class ListTest {
    public static void main(String[] args) {
        List list = new ArrayList();
        //添加书籍数据
        list.add(new Book("红楼梦","曹雪芹",19.9));
        list.add(new Book("三国转","罗贯中",18.8));
        list.add(new Book("西游记","吴承恩",54.1 ));
        list.add(new Book("水浒传","施耐庵",99.6));

        //增强for循环
        System.out.println("=====排序前=====");
        for (Object o :list) {
            System.out.println(o);
        }

        sort(list); //调用静态排序方法
        System.out.println("=====排序后=====");
        for (Object o :list) {
            System.out.println(o);
        }

    }

    public static void sort(List list){
        int length = list.size();
        for (int i = 0; i < length - 1; i++) {
            for (int j = 0; j < length - i - 1; j++) {
                Book book1 = (Book)list.get(j);
                Book book2 = (Book)list.get(j+1);
                if (book1.getPrice() > book2.getPrice()){
                    list.set(j,book2);
                    list.set(j+1,book1);
                }
            }
        }
    }
}
class Book{
    private String name;
    private String author;
    private double price;

    public Book(String name, String author, double price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "名称:"+name+"     "+"价格:"+price+"     "+"作者:"+author;
    }
}

ArrayList底层结构和源码分析

  1. ArrayList中维护了一个Object类型的数组elementData    transient Object[] elementData; // transient 表示瞬间,短暂的,表示该属性不会被序列号
  2. 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍
  3. 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍

Vector底层结构

Vector的基本介绍

  1. Vector类的定义和说明
  2. Vector底层也是一个对象数组,protected Object[] elementData;
  3. Vector 是线程同步的,即线程安全,Vector类的操作方法带有 synchronized
  4. 在开发中,需要线程同步安全时,考虑使用Vector

Vector和ArrayList的比较

08a32174591b4e7c90e42cda4e682fd3.png

LinkedList底层结构

LinkedList的全面说明

  1. LinkedList底层实现了双向链表和双端队列的特点
  2. 可以添加任意元素(元素可以重复),包括 null
  3. 线程不安全,没有实现同步

LinkedList的底层操作机制

  1. LinkedList底层维护了一个双向链表
  2. LinkedList中维护了两个属性first和last,分别指向首节点和尾节点
  3. 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向最后一个节点,最终实现双向链表
  4. LinkedList元素的添加和删除,不是通过数组完成的,相对来说效率较高

示例代码:

//定义一个Node类 Node对象 表示双向链表的一个节点
class Node{
    public Object item; //真正存放数据
    public Node next; //指向下一个节点
    public Node pre; //指向上一个节点
    public Node(Object name){
        this.item = name;
    }
    @Override
    public String toString() {
        return "Node name = " + item;
    }
}
public class LinkedList {
    public static void main(String[] args) {
        //模拟一个简单的双向链表
        Node jack = new Node("jack");
        Node tom = new Node("tom");
        Node smith = new Node("smith");

        //连接三个节点 形成双向链表
        // jack -> tom -> smith
        jack.next = tom;
        tom.next = smith;
        // smith -> tom -> jack
        smith.pre = tom;
        tom.pre = jack;

        //头部指向jack(首节点)
        Node first = jack;
        //尾部指向smith(尾节点)
        Node last = smith;

        //演示链表的添加对象/数据 要求:在tom和smith之间插入一个"hyk"
        //创建Node节点
        Node hyk = new Node("hyk");
        hyk.next = smith; //hyk的下一个是smith
        hyk.pre = tom;  //hyk的上一个是tom
        smith.pre = hyk; //smith的上一个是hyk
        tom.next = hyk; //tom的下一个是hyk

        //演示从头到尾进行遍历
        while (true){
            if (first == null){
                break;
            }
            //输出first信息
            System.out.println(first);
            first = first.next; //first为jack,它的下一个是tom,由此类推
        }
        System.out.println("=================================");
        //从尾到头进行遍历
        while (true){
            if (last == null){
                break;
            }
            System.out.println(last);
            last = last.pre;
        }
    }
}

ArrayList和LinkedList的比较 

cb57007e0c964520a5077e2cc3b95904.png

 Set接口和常用方法

Set接口基本介绍

1. 无序,(添加和取出的顺序不一致),没有索引

2. 不允许有重复元素,所以最多包含一个null

3. JDK API中Set接口的实现类有:

622a79bc7a1343489d57f78e24146980.png

Set接口的常用方法 

和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样

Set接口的遍历方式

同Collection的遍历方式一样,因为Set接口是Collection接口的子接口

1.可以使用迭代器

2.增强for循环

3.不能使用索引的方式来获取

public class SetMethod {
    public static void main(String[] args) {
        //以Set接口的实现类HashSet来创建一个新的实例
        Set set = new HashSet();
        //添加数据
        set.add("jack");
        set.add("peter");
        set.add("jack");//重复的数据
        set.add("tom");
        set.add(null);
        set.add(null);//重复的null
        /*
        1.Set接口实现类hashSet的对象,不能存放重复的元素
        2.只能添加一个null,并且存放的数据是一致的,即添加
        的顺序和取出的顺序不一致
        3.取出的顺序虽然和添加的顺序不一致,但它是固定的
         */
        System.out.println(set);
        //输出:[null, tom, peter, jack]

        //Set的遍历
        // 方式一:使用迭代器
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.print(next+"\t");
            //输出:null	tom	 peter	jack
        }
        System.out.println("");
        //方式二:增强for循环
        for (Object o : set) {
            System.out.print(o+"\t");
            //输出:null	tom	 peter	jack
        }
    }
}

 HashSet的全面说明

1. HashSet实现了Set接口

2.HashSet实际上是HashMap,源码如下:

public HashSet(){
     map = new HashMap<>();
 }

3.可以存放null,但是只能有一个

4.HashSet不保证元素是有序的,取决于hash后,再确定索引的结果(即不保证存放元素的顺序和取出顺序一致)

5.不能有重复的元素/对象

public class HashSet_ {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        System.out.println(set.add("jack")); //t
        System.out.println(set.add("tom")); //t
        System.out.println(set.add("jack")); //f
        System.out.println(set.add(null)); //t

        set.remove("jack");
        System.out.println("set="+set);
        //set=[null, tom]

        set = new HashSet();
        System.out.println(set.add("lucy"));//t
        System.out.println(set.add("lucy"));//f
        set.add(new Dog("tom"));//t
        set.add(new Dog("tom"));//t
        System.out.println("set="+set);
        //输出:set=[tom, tom, lucy]
        set.add(new String("smith")); //成功
        set.add(new String("smith")); //失败
        set.add(new StringBuffer("smith")); //成功
        set.add(new StringBuilder("smith")); //成功
        System.out.println(set);
        //输出:[smith, smith, tom, tom, lucy, smith]
    }
}

class Dog{
    private String name;
    public Dog(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return name;
    }
}

 HashSet底层机制说明

HashSet底层是HashMap,而HashMap的底层是(数组+链表+红黑树)

模拟简单的数组+链表结构

public class HashSetBottom {
    public static void main(String[] args) {
        //模拟一个HashSet的底层 (HashMap的底层结构)
        //1.创建一个数组 数组的类型是Node[]
        //2.Node[]数组也可以直接称为表
        Node[] table = new Node[16];
        System.out.println("table="+table);
        //3.创建节点
        Node john = new Node("john",null);
        table[2] = john;
        Node jack = new Node("jack", null);
        john.next = jack; //将 jack 节点挂载到 john
        Node rose = new Node("rose", null);
        jack.next = rose; //将 rose 节点挂载到 jack

        Node lucy = new Node("lucy", null);
        table[3] = lucy; //把lucy放在table表索引为3的位置
        System.out.println("table="+table);
    }
}

class Node{ //节点 存储数据 可以指向下一个节点 从而形成链表
    Object item; //存放数据
    Node next; //指向下一个节点

    public Node(Object item, Node next) {
        this.item = item;
        this.next = next;
    }
}

1.HashSet 底层是 HashMap

2.添加一个元素时,先得到hash值,会转成索引值

3.找到存储数据表table,看这个索引位置是否已经存放元素

4.如果没有,直接加入

5.如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后

6.在Java8中,如果一条链表的元素个数到达 TREEIFY_THRESHOLD(默认是8),并且table的大小 >= MIN_TREEIFY_CAPACITY(默认64),就会进行数化(红黑树)       

LinkedHashSet的全面说明

1.LinkedHashSet 是 HashSet 的子类

2.LinkedHashSet 底层是一个 LinkedHashMap,底层维护了一个 数组+双向链表

3.LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,同时使用链表维护元素的次序(图),使得元素看起来是以插入顺序保存的

4.LinkedHashSet 不允许添加重复元素

                                                                                                                                          

Map 

 Map框架图

264b81bb0b7345d1967139747210e78e.png

Map接口和常用方法

Map接口实现类的特点

1.Map与Collection并列存在,用于保存具有映射关系的数据:Key-Value(双列元素)

2.Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中

3.Map中的key不允许重复,原因和HashSet一样

4.Map中的value可以重复

5.Map中的key可以为null,value也可以为null,注意key为null只能有一个,value为null可以多个

6.常用String类作为Map的key

7.key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value

public class MapDemo {
    public static void main(String[] args) {
        //1.Map与Collection并列存在,用于保存具有映射关系的数据:Key-Value(双列元素)
        //2.Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
        Map map = new HashMap();
        map.put("n01","青蛙");//K-Y
        map.put("n02","孙尚香");//K-Y
        map.put("n03","貂蝉");//K-Y
        System.out.println(map);
        //输出:{n03=貂蝉, n02=孙尚香, n01=青蛙}

        //3.Map中的key不允许重复,原因和HashSet一样
        map.put("n01","牛马");
        System.out.println(map);
        //输出:{n03=貂蝉, n02=孙尚香, n01=牛马}
        //当有相同的key 就等价于替换   青蛙 ——> 牛马

        //4.Map中的value可以重复
        map.put("n04","貂蝉");
        System.out.println(map);
        //输出:{n03=貂蝉, n02=孙尚香, n04=貂蝉, n01=牛马}

        //5.Map中的key可以为null,value也可以为null,注意key为null只能有一个,value为null可以多个
        map.put(null,"后羿");//不输出
        map.put(null,"嫦娥"); //只输出一个 因为 key为null只能有一个
        map.put("n04",null);
        map.put("n05",null); //value为null可以多个
        System.out.println(map);
        //输出:{n03=貂蝉, null=嫦娥, n02=孙尚香, n05=null, n04=null, n01=牛马}

        //6.常用String类作为Map的key
        map.put(1,"孙策");
        map.put(new Object(),"小乔");
        map.put(2,"大桥");//其它类型也可以
        System.out.println(map);
        //{n03=貂蝉, null=嫦娥, n02=孙尚香, 1=孙策, n05=null, 2=大桥, n04=null, java.lang.Object@1b6d3586=小乔, n01=牛马}

        //7.key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
        System.out.println(map.get("n02")); //通过get方法传入key,会返回对应的value
        //输出:孙尚香
    }
}

8.Map存放数据的key-value示意图,一对k-v是放在HashMap$Node中的,因为Node实现了Entry接口(也可以说一对k-v就是一个Entry)

Map体系继承图

7cb09a31404a4617b6cd4af26897aea7.png

Map常用方法

public class MapMethod {
    public static void main(String[] args) {
        //演示Map接口常用方法
        Map map = new HashMap();
        map.put("邓超",new Book("勇气",10000));
        map.put("邓超","孙俪"); //t
        map.put("宋喆","马蓉");//t
        map.put("王宝强","马蓉");//t
        map.put("蔡徐坤",null);//t
        map.put(null,"刘亦菲");//t
        map.put("鹿晗","关晓彤");//t

        System.out.println(map);
        //{邓超=孙俪, 宋喆=马蓉, null=刘亦菲, 王宝强=马蓉, 蔡徐坤=null, 鹿晗=关晓彤}

        //remove:根据键删除映射关系
        map.remove(null);
        System.out.println(map);
        //输出:{邓超=孙俪, 宋喆=马蓉, 王宝强=马蓉, 蔡徐坤=null, 鹿晗=关晓彤}

        //get:根据键获取值
        Object val = map.get("鹿晗");
        System.out.println(val); //关晓彤

        //size:获取元素个数
        System.out.println(map.size()); //5

        //isEmpty:判断个数是否为0
        System.out.println(map.isEmpty()); //false

        //containsKey:查找键是否存在
        System.out.println(map.containsKey("鹿晗")); //true

        //clear:清除
        map.clear();
        System.out.println(map); //{}
    }
}

class Book{
    private String name;
    private int number;

    public Book(String name, int number) {
        this.name = name;
        this.number = number;
    }
}

 Map接口的遍历方法

public class MapFor {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("邓超","孙俪");
        map.put("宋喆","马蓉");
        map.put("王宝强","马蓉");
        map.put("蔡徐坤",null);
        map.put(null,"刘亦菲");
        map.put("鹿晗","关晓彤");
        //先取出所有的 Key,通过 Key 取出对应的 Value
        Set keySet = map.keySet();
        //1.增强for
        System.out.println("============第一种方式============");
        for (Object key : keySet) {
            System.out.println(key+"-"+map.get(key));
        }
        //2.迭代器
        System.out.println("============第二种方式============");
        Iterator iterator = keySet.iterator();
        while (iterator.hasNext()) {
            Object key =  iterator.next();
            System.out.println(key+"-"+map.get(key));
        }
        //把所有的values取出
        System.out.println("============取出所有的value 增强for============");
        Collection values = map.values();
        //可以使用所有的Collections使用的遍历方法
        //1.增强for
        for (Object value : values) {
            System.out.println(value);
        }
        //2.迭代器
        System.out.println("============取出所有的value 迭代器============");
        Iterator iterator1 = values.iterator();
        while (iterator1.hasNext()) {
            Object value =  iterator1.next();
            System.out.println(value);
        }
        //通过Entry来获取 k-v
        System.out.println("============使用EntrySet 的 for增强============");
        Set entrySet = map.entrySet(); //EntrySet<Entry<K,V>>
        //1.增强for
        for (Object entry : entrySet) {
           //将entry 转成 Map.Entry
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey()+"-"+m.getValue());
        }
        //2.迭代器
        System.out.println("============使用EntrySet 的 迭代器============");
        Iterator iterator2 = entrySet.iterator();
        while (iterator2.hasNext()) {
            Object next =  iterator2.next();
            Map.Entry m = (Map.Entry) next;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
    }
}
  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

CtrlCV 攻城狮

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

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

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

打赏作者

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

抵扣说明:

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

余额充值