【数据结构、List、Set、Collections】

【数据结构、List、Set、Collections】

数据结构

数据存储的常用结构有:栈、队列、数组、链表和红黑树。

栈结构用四个字形容就是:后进先出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XGFBAOyp-1590548762944)(C:\Users\Ann\Desktop\java学习文档\笔记\picture\1559524949123931.png)]

队列

队列 结构简单地说就是:先进先出。即,存进去的元素,要在它前面的元素依次取出后,才能取出该元素。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vdNTAZXp-1590548762945)(C:\Users\Ann\Desktop\java学习文档\笔记\picture\timg.jpg)]

数组

数组Array,是有序的元素序列,数组 是在内存中开辟一段连续的空间,并在此空间存放元素。

简单地说,采用数组结构的集合,对元素的存取有如下的特点:

  • 查找元素快:通过索引,可以快速访问指定位置的元素。
    • 在内存中,数组的数据是连接存储的,数据长度固定,这样知道数组开头位置和偏移量就可以直接算出数据地址。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XRKmw3cD-1590548762946)(C:\Users\Ann\Desktop\java学习文档\笔记\picture\array1.png)]

  • 增删元素慢:
    • 指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置。并将新数组的地址值赋值给原变量,最后将源数组在内存中销毁(垃圾回收)。
    • 在堆内存中,频繁的创建数组,赋值 数组中的元素,销毁数组,效率低下。

链表

链表linked list:由一系列结点node(链表中每一个元素成为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:**一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。**我们常说的链表结构有单向链表和双向链表。

特点:

  • 查询慢:链表中地址不是连续的,每次查询元素,都必须从头开始查询。
  • 增删快:链表结构,增加/删除一个元素,对链表的整体结构没有影响,所以增删快。

**单向链表:**链表中只有一条链子,不能保证元素的顺序(存储元素和取出元素的顺序有可能不一样)。

**双向链表:**链表中有两条链子,有一条链子是专门记录元素的顺序,是一个有序的集合。

红黑树

二叉树:binary tree,是每个结点不超过2的有序树(tree)

简单的理解,就是一种类似于我们生活中树的结构,只不过每个结点都最多只能有两个子节点。

二叉树是每个节点最多只能有两个子树的结构,顶上的叫根结点,两边被称作“左子树”和“右子树”。

在二叉树的基础上,元素是有大小顺序的,左子树小,右子树大,这称为顺序树/查找树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XgCsCyMq-1590548762946)(C:\Users\Ann\Desktop\java学习文档\笔记\picture\红黑树.jpg)]

在二叉树中,左子树的数量与右子树的数量一样时,就是平衡树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RpYovDEU-1590548762947)(C:\Users\Ann\Desktop\java学习文档\笔记\picture\平衡树.jpg)]

红黑树,特点:

  • 趋近于平衡树,查询的速度非常的快,查询叶子节点最大次数和最小次数不能超过两倍。
  • 约束:
    • 1.节点可以是红色的或者黑色的;
    • 2.根节点是黑色的;
    • 3.叶子节点(空节点)是黑色的;
    • 4.每个红色的节点的子节点都是黑色的;
    • 5.任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ahk2SoFV-1590548762948)(C:\Users\Ann\Desktop\java学习文档\笔记\picture\红黑树1.jpg)]

List集合

Collection接口有两个子接口:List接口、Set接口。

List接口的三大特点:

  1. 有序的集合,存储元素和取出元素的顺序是一致的(存储123,取出123);
  2. 有索引,包含了一些带索引的方法;
  3. 允许存储重复的元素。

List接口中带索引的方法:

方法含义
public void add(int index,E element);将指定的元素,添加到该集合中的指定位置上
public E get(int index);返回集合中指定位置的元素
public E remove(int index);溢出列表中指定位置的元素,返回的是被移除的元素
public E set(int index,E element);用指定元素替换集合中指定位置的元素,返回的是更新前的元素

注意:操作索引的时候,一定要防止索引的越界异常。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Demo01List {
    public static void main(String[] args) {
        //创建一个List对象,泛型为String,使用多态
        List<String> list = new ArrayList<>();
        //使用add方法往集合中添加元素
        list.add("James");
        list.add("Kobe");
        list.add("Durant");
        list.add("Harden");
        list.add("Allen");
        //打印集合
        System.out.println(list);//[James, Kobe, Durant, Harden, Allen],重写了toString方法

        //add(int index,E element)方法
        //在Durant之后插入Curry
        list.add(3,"Curry");
        System.out.println(list);//[James, Kobe, Durant, Curry, Harden, Allen]

        //remove(int index)方法,返回的是被移除的元素
        //移除Allen
        String allen = list.remove(5);
        String kobe = list.remove(1);
        System.out.println("已经退役的球员:"+allen+"、"+kobe);//已经退役的球员:Allen、Kobe
        System.out.println(list);//[James, Kobe, Durant, Curry, Harden]

        //set(int index,E element),返回的是被替换的元素
        //将Harden替换为Leonard
        String set = list.set(3, "Leonard");
        System.out.println("被替换的球员是:"+set);//被替换的球员是:Harden
        System.out.println(list);//[James, Durant, Curry, Leonard]

        //get(int index),返回集合中当前位置的元素
        //使用for循环遍历
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        System.out.println("----------------");
        //使用迭代器遍历
        Iterator<String> iterator = list.iterator();
        while(iterator.hasNext()){
            String s = iterator.next();
            System.out.println(s);
        }
        System.out.println("----------------");
        //使用增强for循环
        for(String s:list){
            System.out.println(s);
        }
    }
}

List接口的实现类

ArrayList集合

Java.util.ArrayList集合数据存储的结构是数组结构的,元素增删慢,查找快,由于日常开发中使用的最多的功能为查询数据,遍历数据,所以ArrayList是最常用的集合。

许多程序员开发时非常随意的使用ArrayList完成任何需求, 并不严谨,这种用法是不提倡的。

LinkedList集合

Java.util.LinkedList集合数据存储的结构是链表结构,方便添加、删除元素。

LinkedList的特点:

  1. 底层是一个链表结构:查询慢,增删快;
  2. 里面包含了大量的操作首尾元素的方法;
    • 注意:使用LinkedList集合的特有方法,不能使用多态创建对象

LinkedList特有的方法:

方法含义类型
public void addFirst(E e);将指定元素插入此列表的开头添加元素
public void addLast(E e);将指定的元素添加到此列表的末尾,等效于add添加元素
public void push(E e);将元素推入此列表所表示的堆栈,等效于addFirst添加元素
public E getFirst();返回此列表的第一个元素获取元素
public E getLast();返回此列表的最后一个元素获取元素
public E removeFirst();移除并返回此列表的第一个元素删除元素
public E removeLast();移除并返回此列表的最后一个元素删除元素
public E pop();从此列表所表示的堆栈处弹出一个元素,等效于removeFirst删除元素
public boolean isEmpty();如果列表不包含元素,则返回true查询列表状态
import java.util.LinkedList;

public class Demo01LinkedList {
    public static void main(String[] args) {
        //创建一个LinkedList集合对象
        LinkedList<String> linked = new LinkedList<>();
        //使用add方法往集合中添加元素
        linked.add("a");
        linked.add("b");
        linked.add("c");
        System.out.println(linked);//[a, b, c]
        addElement(linked);
        getElement(linked);
        removeElement(linked);
        //判断集合是否为空。
        boolean emptyOrNot = linked.isEmpty();
        System.out.println("集合是否为空:"+emptyOrNot);


    }
    public static  void addElement(LinkedList<String> linked){
        //addFirst(E e):将指定的元素插入此列表的开头
        linked.addFirst("0");
        System.out.println(linked);//[0, a, b, c]
        //addLast(E e):将指定的元素插入此列表的末尾,此方法等效于add方法
        linked.addLast("1");
        System.out.println(linked);//[0, a, b, c, 1]
        //push(E e):将元素推入此列表所表示的堆栈,等效于addFirst
        linked.push("w");
        System.out.println(linked);//[w, 0, a, b, c, 1]

    }
    public static void getElement(LinkedList<String> linked){
        //getFirst():返回此列表的第一个元素
        String first = linked.getFirst();
        System.out.println(first);//w
        //getLast():返回此列表的最后一个元素
        String last = linked.getLast();
        System.out.println(last);//1
    }
    public static void removeElement(LinkedList<String> linked){
        //removeFirst():移除并返回此列表的第一个元素
        String s = linked.removeFirst();
        System.out.println("移除第一个元素"+s);
        System.out.println(linked);
        //removeLast():移除并返回列表的最后一个元素
        String s1 = linked.removeLast();
        System.out.println("移除最后一个元素"+s1);
        System.out.println(linked);
        //pop():从此列表所表示的堆栈处弹出一个元素,相当于removeFirst
        String pop = linked.pop();
        System.out.println("从堆栈中弹出一个元素"+pop);
        System.out.println(linked);


    }
}
Vector集合(了解)

Vector类集合可以实现可增长的对象数组。Vector类型是同步的,是单线程的,速度慢。在JDK 1.2以后被ArrayList类集合取代。

Set集合

Set接口的特点:

  1. 不允许存储重复的元素;
  2. 没有索引,没有带索引的方法,也不能使用普通的for循环遍历。

Set接口的实现类

HashSet集合

此类实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set的迭代顺序:特别是不保证该顺序恒久不变。此类允许使用null元素。

Java.util.HashSet集合 implements Set接口。

HashSet特点:

  1. 不允许存储重复的元素;
  2. 没有索引,没有带索引的方法,也不能使用普通的for循环遍历;
  3. 是一个无序的集合,存储元素和取出元素的顺序有可能不一致;
  4. 底层是一个哈希表结构(查询的速度非常的快)。
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Demo01HashSet {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        //使用add方法往集合中添加元素
        set.add(3);
        set.add(2);
        set.add(5);
        set.add(3);
        //使用迭代器遍历set集合
        Iterator<Integer> iterator = set.iterator();
        while(iterator.hasNext()){
            Integer next = iterator.next();
            System.out.println(next);//2,3,5
        }
        //使用增强for循环遍历set集合
        for (Integer integer : set) {
            System.out.println(integer);//2,3,5

        }
    }
}
哈希值

哈希值是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址。是模拟出来得到的地址,不是数据实际存储的物理地址。

在Object类有一个方法可以获取对象的哈希值。

int hashCode():返回该对象的哈希码值。

hashcode方法的源码:

public native int hashCode();

​ native:代表该方法调用的是本地操作系统的方法。

public class Demo01HashCode {
    public static void main(String[] args) {
        //Person类继承了Object类,所以可以使用Object类的hashCode方法
        Person p1 = new Person();
        int h1 = p1.hashCode();
        System.out.println(h1);//0

        Person p2 = new Person();
        int h2 = p2.hashCode();
        System.out.println(h2);//0

        System.out.println(p1);//an.kaikeba.demo01.Person@0
        System.out.println(p2);//an.kaikeba.demo01.Person@0
        System.out.println(p1 == p2);//实际物理地址值不相等

        //String类的哈希值
        //String类重写了Object类的hashCode方法
        String str1 = new String("abc");
        String str2 = new String("abc");
        System.out.println(str1 == str2);//false
        System.out.println(str1.hashCode());//96354
        System.out.println(str2.hashCode());//96354
        //重地与通话的哈希值一样
        System.out.println("重地".hashCode());
        System.out.println("通话".hashCode());

    }
}
哈希表

HashSet集合存储数据的结构(哈希表),特点是查询快。

jdk1.8版本之前,哈希表 = 数组 + 链表

jdk1.8版本之后:

  • 哈希表 = 数组 + 链表
  • 哈希表 = 数组 + 红黑树(提高查询的速度)

哈希表数组结构,把元素进行分组,相同哈希值的元素是一组,链表/红黑树结构把相同哈希值的元素连接到一起。

  • 数组更利于元素的查找;
  • 链表更利于元素的插入和删除;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-poIGp8YM-1590548762949)(C:\Users\Ann\Desktop\java学习文档\笔记\picture\哈希表.jpg)]

哈希值不同的元素,放入数组的不同位置。

两个元素不同,但是哈希值相同,成为哈希冲突。两个不同的元素,将会以链表的形式进行连接。

如果数组的同一位置存放的数据超过8位,会以红黑树的结构进行存储。

Set集合存储元素不重复的原理

Set集合在调用add方法的时候,add方法会调用元素的hashCode方法和equals方法,判断元素是否重复。

import java.util.HashSet;

public class Demo01HashSetSaveString {
    public static void main(String[] args) {
        //创建HashSet集合对象
        HashSet<String> set = new HashSet<>();
        String str1 = new String("abc");
        String str2 = new String("abc");
        set.add(str1);
        set.add(str2);
        set.add("重地");
        set.add("通话");
        set.add("abc");
        System.out.println(set);//[重地, 通话, abc]
    }
}

对于set.add(str1);

add方法会调用str1的hashCode方法,计算字符串“abc”的哈希值,哈希值是96354。

在几个中找有没有96354这个哈希值的元素,发现没有,就会把str1存储到集合中。

对于set.add(str2);

add方法会调用str2的hashCode方法,计算字符串“abc”的哈希值,哈希值是96354。

在集合中找有没有96354这个哈希值的元素,发现有(哈希冲突)。

str2会调用equals方法和哈希值相同的元素进行比较,str2.equals(str1),返回true

两个元素的哈希值相同,equals方法返回true,认定两个元素相同。

就不会把str2存储到集合中。

对于set.add(“重地”);

add方法会调用”重地“的hashCode方法,计算字符串”重地“的哈希值,哈希值是1179395。

在几个中找有没有1179395这个哈希值的元素,发现没有,就会把”重地“存储到集合中。

对于set.add(“通话”);

add方法会调用"通话"的hashCode方法,计算字符串"通话"的哈希值,哈希值是1179395。

在几个中找有没有1179395这个哈希值的元素,发现有(哈希冲突)。

”通话“会调用equals方法和哈希值相同的元素进行比较,”通话“.equals(”重地“),返回false

两个元素的哈希值相同,equals方法返回false,认定两个元素不同。

就会把“通话”存储到集合中。

**【重点】**set集合存储元素,要保证元素不重复,前提是:

存储的元素必须重写hashCode方法和equals方法。

HashSet存储自定义类型元素

给HashSet中存放自定义类型元素时,需要重写对象中的hashCode方法和equals方法,建立自己的比较方式,才能保证HashSet集合中对象唯一。

创建自定义类Student类,对该类的hashCode方法和equals方法进行重写。

import java.util.Objects;

public class Student {
    private String name;
    private int age;

    public Student() {

    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    public 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 int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
import java.util.HashSet;

public class Demo01HashSetSaveString {
    public static void main(String[] args) {
        //创建HashSet集合存储Student
        HashSet<Student> set = new HashSet<>();
        Student p1 = new Student("小美女",18);
        Student p2 = new Student("小美女",18);
        Student p3 = new Student("小野兽",22);
        System.out.println(p1.hashCode());//734175839
        System.out.println(p2.hashCode());//734175839

        System.out.println(p1 == p2);//false
        System.out.println(p1.equals(p2));//true
        set.add(p1);
        set.add(p2);
        set.add(p3);
        System.out.println(set);
        //[Student{name='小美女', age=18}, Student{name='小野兽', age=22}]
    }
}
LinkedHashSet集合

Java.util.LinkedHashSet集合 extends HashSet集合。

具有可预知迭代顺序的Set接口的哈希表和链接列表实现。此实现与HashSet的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入set的顺序(插入顺序)进行迭代。

LinkedHashSet集合特点:

  • 底层是一个哈希表(数组+链表/红黑树)+ 链表:多了一条链表,记录元素的存储顺序,保证元素有序
import java.util.HashSet;
import java.util.LinkedHashSet;

public class Demo01LinkedHashSet {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("www");
        set.add("www");
        set.add("abc");
        set.add("it");
        System.out.println(set);//[abc, www, it],无序,不允许重复

        LinkedHashSet<String> set1 = new LinkedHashSet<>();
        set1.add("www");
        set1.add("www");
        set1.add("abc");
        set1.add("it");
        System.out.println(set1);//[www, abc, it],有序,不允许重复
        
    }
}

可变参数

可变参数:jdk1.5之后出现的新特性。

使用前提:

  • 当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数。

使用格式:定义方法是使用。

修饰符 返回值类型 方法名(数据类型 … 变量名){}

可变参数的原理:

  • 可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数。
  • 传递参数的个数,可以是0个(不传递),1,2,…多个
public class Demo01VarArgs {
    public static void main(String[] args) {

        int s = sum(10, 20, 30);
        System.out.println(s);//60
    }

    //定义计算(0~n)整数和的方法
    //已知:计算整数的和,数据类型已经确定int
    //但是参数的个数不确定,不知道要计算几个整数的和,就可以使用可变参数
    public static int sum(int... array) {
        int s = 0;
        for (int i : array) {
            s += i;

        }
        return s;
    }
}

可变参数的注意事项:

  1. 一个方法的参数列表,只能有一个可变参数;
  2. 如果方法的参数有多个,那么可变参数必须写在参数列表的末尾。
public static void print(int a,double b,String c,char ...d){
        //...
    }

可变参数的终极用法:

public static void print(Object ...o){
        //...
    }

Collections

java.utils.Collections是集合工具类,用来对集合进行操作。部分方法如下:

方法含义
public static boolean addAll(Collections c,T …elements);往集合中添加一些元素
public static void shuffle(List list);打乱集合中元素顺序
public static void sort(List list);将集合中元素按照默认规则排序
public static void sort(List list,Comparator<? super T>)将集合中元素按照指定规则排序

addAll方法&shuffle方法

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

public class Demo01Collections {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"a","b","c","d");
        System.out.println(list);//[a, b, c, d]

        Collections.shuffle(list);
        System.out.println(list);//[d, b, c, a]
    }
}

sort(List)方法

public static <T> void sort(List<T> list);:将集合中元素按照默认规则排序。

import java.util.ArrayList;
import java.util.Collections;

public class Demo01sort {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"A","a","0");
        System.out.println(list);//[A, a, 0]

        //将集合中的元素按照默认规则排序,默认是升序
        Collections.sort(list);
        System.out.println(list);//[0, A, a]

        ArrayList<Integer> list2 = new ArrayList<>();
        Collections.addAll(list2,2,78,33);
        System.out.println(list2);//[2, 78, 33]

        //将集合中的元素按照默认规则排序,默认是升序
        Collections.sort(list2);
        System.out.println(list2);//[2, 33, 78]
    }
}

注意事项:如果采用sort方法对元素为自定义类的对象的集合进行排序,该类需要实现Comparable接口,并重写里面的compareTo方法,定义排序的规则。

Comparable接口的排序规则:

自己(this)- 参数:升序

参数 - 自己(this):降序

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Person{name='" + this.name + "\'" + ",age=" + this.age + "}";
    }

    public Person() {
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Person o) {
        //自定义比较的规则,比较两个人的年龄(this,参数Person)
        return (this.getAge() - o.getAge());//升序
        //return (o.getAge() - this.getAge());//降序

    }

}
import java.util.ArrayList;
import java.util.Collections;

public class Demo01sort {
    public static void main(String[] args) {
        ArrayList<Person> list = new ArrayList<>();
        Person p1 = new Person("James",35);
        Person p2 = new Person("Durant",30);
        Person p3 = new Person("Liao",25);
        Collections.addAll(list,p1,p2,p3);
        System.out.println(list);
        //[Person{name='James',age=35}, Person{name='Durant',age=30}, Person{name='Liao',age=25}]
        //对对象元素进行排序,排序规则为按年龄升序
        Collections.sort(list);
        System.out.println(list);
        //[Person{name='Liao',age=25}, Person{name='Durant',age=30}, Person{name='James',age=35}]
    }
}

sort(List,Comparator)方法

Comparator和Comparable的区别

  • Comparator:相当于找一个第三方的裁判,来比较两个对象,Comparator是一个接口,需要实现并重写其compare方法
  • Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法

Comparator接口的排序规则:

o1 - o2:升序

o2 - o1:降序

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Demo01sortComparator {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,2,3,5,7,1,2);
        System.out.println(list);//[2, 3, 5, 7, 1, 2]

        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;//升序
            }
        });
        System.out.println(list);//[1, 2, 2, 3, 5, 7]
    }
}

r是一个接口,需要实现并重写其compare方法

  • Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法

Comparator接口的排序规则:

o1 - o2:升序

o2 - o1:降序

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Demo01sortComparator {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,2,3,5,7,1,2);
        System.out.println(list);//[2, 3, 5, 7, 1, 2]

        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;//升序
            }
        });
        System.out.println(list);//[1, 2, 2, 3, 5, 7]
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值