Java中集合的简单使用

集合

一、集合框架

1.1概念

  • 存放对象的容器,用于存放对象,定义了对多个对象进行操作的常用方法。可以实现类似数组的功能
  • 数组缺点:
    1. 数组定长,一旦带你关于不可被改变
    2. 数组没有方法
    3. 数组只能存放相同类型的数据
  • 集合特点:
    1. 长度可变
    2. 集合中丰富的操作元素的方法
    3. 集合中只能存放引用数据类型的数据
  • 集合的分类
    1. 单列集合(集合中一个元素只能保存一个数据)Collection
    2. 双列集合(集合中一个元素只能保存两个数据)Map

1.2集合框架体系结构

在这里插入图片描述

Collection体系集合

特点:代表一组任意类型的对象。

常用方法:

  • add(Object obj)添加一个对象
  • addAll(Collection c)将一共集合中的所有对象添加到此集合中
  • clear()清空此集合中的所有对象
  • contains(Object o)检查此集合中是否包含o对象
  • equals(Object o)比较此集合是否与指定对象相等
  • isEmpty判断此集合是否为空
  • remove(Object o)在此集合中移除o对象
  • size()返回此集合中的元素个数
  • toArray()将此集合转换成数组
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

public class CollectionDemo {
    public static void main(String[] args) {
        //创建Collection对象
        Collection coll=new ArrayList();


        //add向集合中添加元素
        coll.add("aaa");
        coll.add(12);
        coll.add("bbb");
        System.out.println(coll);

/*
        //清空集合所有元素
        coll.clear();
        System.out.println(coll);
        */


        //判断集合中是否有指定的元素
        System.out.println(coll.contains("ccc"));

        //判断集合否为空
        System.out.println(coll.isEmpty());

        //删除集合中的指定元素
        System.out.println(coll.remove(12));

        //返回集合中有效元素个数
        System.out.println(coll.size());

        //将集合转换成一个数组
        Object[] obj=coll.toArray();

        //数组工具类 将数组转换成字符串
        System.out.println(Arrays.toString(obj));
        
        //数组的工具类 将数组转换成集合
        int[] arr = {1,2,3};
        List<int[]> ints = Arrays.asList(arr);

        
         Collection coll1 = new ArrayList();
        coll1.add("a");
        coll1.add("b");
        coll1.add("c");
        Collection coll2 = new ArrayList();
        coll2.add("1");
        coll2.add("2");
        coll2.add("3");
        //add方法添加会把所有元素当作一个整体元素添加
        //coll1.add(coll2);
        //addAll 将一个的集合的每一个元素添加到指定元素
        coll1.addAll(coll2);
        System.out.println(coll1);

        //containsAll 判断集合中是否包含另一个集合元素
        System.out.println(coll1.containsAll(coll2));

        //删除集合中指定集合的元素
        coll1.removeAll(coll2);
        System.out.println(coll1);
    }
}

1.3集合遍历

  • 使用迭代器遍历
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class IteratorDemo {
    public static void main(String[] args) {
        Collection coll = new ArrayList();
        coll.add("a");
        coll.add("b");
        coll.add("c");
        coll.add("e");
        coll.add("f");
        coll.add("g");
        coll.add("h");
        coll.add("i");
        coll.add("j");
        
		//创建Iterator对象
        Iterator it = coll.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }     
    }
}

使用迭代器需要注意的问题:

  1. 迭代器不能多次使用
  2. 迭代器不能再迭代过程中多次调用next()方法
  3. 迭代器在迭代过程中不要向集合中添加或移除元素

二、List

特点:

  • 元素有序、且可重复的集合,集合中的每个元素都有其对应的顺序索引;
  • 允许使用重复元素,可以通过索引来访问指定位置的集合元素;
  • 默认按元素的添加顺序设置元素的索引。

相关实现类比较:

  • ArrayList
    • 基于数组结构实现,使用在查询比较多的场合下;
    • JDK1.2出现,运行效率高,线程不安全。
  • Vector
    • 基于数组结构实现,使用在查询比较多的场合下;
    • JDK1.0出现,运行效率低,线程安全。
  • LinkedList
    • 基于链表结构实现,增删快,查询慢。

2.1ArrayList基本使用

2.1.1基本操作
  • add(E e)添加元素
  • add(int index, E element)添加元素到指定位置,ArrayList元素下标从0开始
  • size()获取ArrayList长度
  • get(int index)获取指定位置的元素
  • addAll(Collection<? extends E> c)添加另外一个集合
  • addAll(int index,Collection<? extends E> c)添加另外一个集合到指定位置
  • clear()清空
  • set(int index, E element)设置某个位置的元素
  • contains(Object o)判断集合中是否包含某个元素
  • remove(int index)删除指定位置的元素
  • remove(Object o)删除特定元素
  • indexOf(Object o)返回元素的索引
import java.util.ArrayList;

public class ArrayListDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        list.add("aaa");
        list.add("aaa");
        list.add("xxx");
        list.add("bbb");
        list.add("ccc");
        System.out.println(list);

        //list集合就是在Collection接口的基础上添加了一下带下标索引的方法
        //向集合添加元素到指定位置
        list.add(2,"zzz");
        System.out.println(list);

        //修改指定位置下标位置上的元素
        list.set(2,"ooo");
        System.out.println(list);

        //删除指定下标位置上的元素信息
        list.remove(1);
        System.out.println(list);

        //查找元素首次出现的索引
        System.out.println(list.indexOf("aaa"));
    }
}
2.1.2ArrayList遍历

ArrayList遍历的方式:

  • 普通for循环 — while循环
  • 增强for循环
  • 使用迭代器遍历
import java.util.ArrayList;
import java.util.Iterator;

public class ArrayListDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        list.add("aaa");
        list.add("aaa");
        list.add("xxx");
        list.add("bbb");
        list.add("ccc");
        System.out.println(list);
        
        //通过下标遍历集合
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

        //通过增强for循环
        /*
        * for(遍历项的数据类型 遍历项:要遍历的集合和数组)
        * {
        *   打印语句
        * }
        * */
        for (String Str : list) {
            System.out.println(Str);
        }
        
        //通过迭代器遍历集合
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

2.2LinkedList集合

特点:增删快遍历慢

2.2.1基本操作
  • add(E e) 将指定元素添加到此列表的结尾0
  • add(int index, E element) 在此列表中指定的位置插入指定的元素
  • addFirst(E e) 将指定元素插入此列表的开头
  • addLast(E e) 将指定元素添加到此列表的结尾
  • clear() 从此列表中移除所有元素
  • offer(E e) 将指定元素添加到此列表的末尾(最后一个元素)
  • peek() 获取但不移除此列表的头(第一个元素)
  • poll() 获取并移除此列表的头(第一个元素)
  • pop() 从此列表所表示的堆栈处弹出一个元素
  • push(E e) 将元素推入此列表所表示的堆栈
  • size() 返回此列表的元素数

2.3VectorList集合

基本操作与 ArrayList 集合一致

vectorArrayList的区别
  1. Vector ArrayList 底层都是数组
  2. Vector 初始化是就创建了长度为10的数组 ArrayList在第一次添加元素的时候进行扩容
  3. Vector线程安全
LinkedList与Vector和ArrayList区别

LinkedList增删快遍历慢 Vector和ArrayList遍历快增删慢

三、Set

Set接口是Collection的子接口,Set接口没有提供额外的方法。

Set集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个Set集合中,则添加操作失败。

Set判断两个对象是否相同不是使用==运算符,而是根据equals()方法。

Set接口实现类比较:

  • HashSet作为Set接口的主要实现类,线程不安全的,可以存储null值;
  • LinkedHashSet作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历,对于频繁的遍历操作,LinkedHashSet效率高于HashSet
  • TreeSet可以按照添加对象的指定属性,进行排序。

3.1、HashSet

HashSetSet接口的典型实现,大多数时候使用Set集合时都使用这个实现类。

HashSetHash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能

HashSet具有以下特点:

  • 不能保证元素的排列顺序;
  • 元素不能重复;
  • HashSet不是线程安全的;
  • 集合元素可以是null

HashSet集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相等,并且两个对象的equals()方法返回值也相等。

对于存放在Set容器中的对象,对应的类一定要重写equals()hashCode()方法,以实现对象相等规则。即:相等的对象必须具有相等的散列码

3.2.1、Set的无序性和不可重复性
  • 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
  • 不可重复性:保证添加的元素按照equals()判断时,不能返回true。即:相同的元素只能添加一个。
//学生类
public class Student {
    private String id;
    private String name;
    private String age;
    
    //有参和无参构造方法
    //get和set方法
    //toString方法
    //equals和hashCode方法
}

public class HsahSetDemo {
    public static void main(String[] args) {
        Student p1 = new Student("001", "Tom", 20);
        Student p2 = new Student("002", "Bob", 21);
        Student p3 = new Student("002", "Bob", 21);
        Student p4 = new Student("003", "Smith", 22);

        HashSet<Student> set = new HashSet<>();
        set.add(p1);
        set.add(p2);
        set.add(p3);
        set.add(p4);

        /*
        	没有重写equals()和hashCode()方法,重复元素能够重复添加。
        */
        for (Student s : set) {
            System.out.println(s);
        }
    }
}

HashSet底层实现原理:HashMap

HashSet中元素的添加过程:

  • 当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的 hashCode()值,然后根据hashCode()值,通过某种散列函数决定该对象在HashSet底层数组中的存储位置。

    • 如果此位置上没有其他元素,则添加成功;
    • 如果此位置上有其他元素,则进行进一步比较。
  • 比较两元素的hashCode()的值:

    • 如果两个元素的hashCode()值不相等,添加成功;

    • 如果两个元素的hashCode()值相等,会再继续调用equals()方法,如果equals()方法结果为true,添加失败;如果为false,那么会添加该元素,但是该数组的位置已经有元素了,那么会通过链表的方式继续链接。

    • HashSet底层去重原理

      • 首先:比较两个对象的Hash值,如果Hash值不同则认为两个对象不重复
      • 如果Hash相同,进而比较equals,如果equals,返回true,则认为两个对象重复,否则认为还是不重复

    HashSet底层结构:数组+链表

    如果两个元素的equals()方法返回true,但它们的hashCode()返回值不相等HashSet将会把它们存储在不同的位置,但依然可以添加成功

3.2TreeSet

TreeSet可以确保集合元素处于排序状态。

TreeSet底层实现原理:TreeMap

  • 去重原理:
  • 根据树没有存放的依据,所以需要借助CompareTo方法,进行比较
  • 去重的根据是通过CompareTo的返回值进行判断,如果返回值为0证明相等,就证明重复 去重
  • CompareTo返回值 大于0升序 小于0降序
public class TreeSetDemo {
    public static void main(String[] args) {
        //默认使用自然排序在TreeSet中排序
        TreeSet<String> set = new TreeSet<>();
        set.add("123");
        set.add("abc");
        set.add("aBc");
        set.add("AA");
        set.add("EE");

        for (String s : set) {
            System.out.println(s);
        }
        System.out.println("-----------------------------------------");

        TreeSet<String> set1 = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return -o1.compareTo(o2);
            }
        });

        set1.add("123");
        set1.add("abc");
        set1.add("aBc");
        set1.add("AA");
        set1.add("EE");

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

3.5Collections常用方法

Collections是一个操作List SetMap等集合的工具类。

Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。

排序操作(均为static方法):

  • reverse(List<?> list):反转List中元素的顺序;

  • shuffle(List<?> list):对List集合元素进行随机排序;

  • sort(List<T> list):根据元素的自然顺序对指定List集合元素按升序排序;

  • sort(List<T> list, Comparator<? super T> c):根据指定的Comparator产生的顺序对List集合元素进行定制排序

  • swap(List<?> list, int i, int j):将指定List集合中的i处元素和j处元素进行交换。

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

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

        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        //将集合元素进行翻转
        Collections.reverse(list);
        System.out.println(list);//ccc, bbb, aaa

        //将集合中的顺序随机打乱
        Collections.shuffle(list);
        System.out.println(list);

    }
}

四、Map

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pywmPSqf-1668416522083)(D:\Folder\笔记\img\Map.png)]

常用方法:

  • 添加、删除、修改操作

    • put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
    • putAll(Map m):将m中的所有key-value对存放到当前map
    • remove(Object key):移除指定keykey-value对,并返回value
    • clear():清空当前Map中的所有数据
  • 查询操作

    • get(Object key):获取指定key对应的value
    • containsKey(Object key):是否包含指定的key
    • containsValue(Object value):是否包含指定的value
    • size():返回mapkey-value对的个数
    • isEmpty():判断当前map是否为空
    • equals(Object obj):判断当前map和参数对象obj是否相等
  • 其他操作

    • keySet():返回所有key构成的Set集合
    • values():返回所有value构成的Collection集合
    • entrySet():返回所有key-value对构成的Set集合

4.1、HashMap

HashMapMap接口使用频率最高的实现类。

  • 允许使用null键和null值,与HashSet一样,不保证映射的顺序;
  • 所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写:equals()hashCode()
//Map基本操作
public class HashMapDemo {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        //添加元素
        map.put("1", "zs");
        map.put("2111", "ls");
        map.put("32211", "ww");

        System.out.println(map);
        System.out.println("---------------------------");
        //添加另一个map
        HashMap<String, String> map1 = new HashMap<>();
        map1.put("4122", "tom");
        map1.put("533", "bob");
        map.putAll(map1);
        System.out.println(map);
        System.out.println("---------------------------");
        //根据指定的Key移除指定的键值对
        map.remove("533");
        System.out.println(map);
        //通过key获取对应的value
        System.out.println(map.get("1"));
        //判断是否包含对应的key
        System.out.println(map.containsKey("1"));
        //判断是否包含对应的value
        System.out.println(map.containsValue("zs"));
        //返回键值对个数
        System.out.println(map.size());
        //判断map是否为空
        System.out.println(map.isEmpty());
        //清空map
        map.clear();
        System.out.println(map);
    }
}

遍历:

public class HashMapDemo {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        //添加元素
        map.put("1", "zs");
        map.put("2111", "ls");
        map.put("32211", "ww");

        /**
         * 遍历map所有的key
         * 1.获取所有的key
         * 2.遍历
         */
        Set<String> set = map.keySet();
        for (String s : set) {
            System.out.println(s);
        }

        /**
         * 遍历map所有的value
         * 1.获取所有的value
         * 2.遍历
         */
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println(value);
        }

        /**
         * 遍历map所有的键值对 方式1
         * 1.获取所有的key
         * 2.遍历key
         * 3.遍历key的过程中获取key对应的value
         */
        Set<String> keys = map.keySet();
        for (String k : keys) {
            String v = map.get(k);
            System.out.println(k + ":" + v);
        }

        /**
         * 遍历map所有的键值对 方式2
         * 1.获取Entry
         * 2.遍历所有的Entry
         */
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        for (Map.Entry<String, String> entry : entrySet) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + ":" + value);
        }
    }
}

案例演示:

//创建一个城市的map集合(山东 青岛 济南 潍坊)
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class Demo01 {
    public static void main(String[] args) {

        HashMap<String, List<String >> map = new HashMap<>();
        ArrayList<String> list = new ArrayList<>();
        list.add("青岛");
        list.add("济南");
        list.add("淄博");
        map.put("山东",list);

        System.out.println(map.get("山东"));
    }
}
put添加流程
  1. 通过扰乱算法,计算一个hash值(目的避免计算的下标重复)
  2. 第一次添加元素会扩容为16的Node数组
  3. 通过下标寻址算法hash值与数组长度-1做运算 计算出一个下标
    1. 当前下标位置上为null,直接创建一个Node节点,并放到下标位置
    2. 当前位置不为null
      1. 判断添加元素是否与之前的元素相等(hashcode和equals)如果相等则覆盖,并返回旧值
      2. 如果当前元素是红黑树,那么元素直接添加到红黑树上
      3. 如果当前元素上位链表,会添加到链表尾部(尾插法)
        • 链表会树化的条件:
          1. 链表长度大于8
          2. 数组长度大于64

4.2Hashtable

Hashtable与HashMap用法基本一致

  • HashMap与Hashtable区别
    1. HashMap K V 可以Null Hashtable K V不可以为Null
    2. HashMap线程不安全 Hashtable 线程安全(以后就算考虑线程安全安全 也不会用这个类 用ConcurrentHashMap)

五、泛型

4.1概述

泛型可以理解为标签

  • 中药店,每个抽屉外面贴着标签;
  • 超市购物架上很多瓶子`,每个瓶子装的是什么,有标签。

集合在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为ObjectJDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型Collection<E>List<E>ArrayList<E> 这个<E>就是类型参数,即泛型

所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。

JDK1.5以后,Java引入了参数化类型(Parameterized type)的概念,允许我们在创建集合时再指定集合元素的类型,正如:List<String>,这表明该List只能保存字符串类型的对象。

JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参

4.2为什么要有泛型

  • 解决元素存储的安全性问题
  • 解决获取数据元素时,需要类型强制转换的问题

集合中没有泛型时:

  • String类型对象添加到集合中,在集合中当成Object类型对象
  • 从集合中获取,读取到的对象只能被当成Object类的对象读取
  • 如果我们希望将读取到的对象当成String类的对象使用,需要向下转型
  • 转换过程繁琐,而且可能出现ClassCastException异常

集合中有泛型时:

  • String类型对象添加到集合中,在集合中当成String类型对象;
  • 从集合中获取,读取到的对象被当成String类的对象读取,不需要向下转型

Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮

4.3泛型类及泛型接口

  • 泛型的声明:

    • interface List<T>class GenTest<K,V> 其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。常用T表示,是Type的缩写
//定义带泛型的类
public class MyClass1<T> {
    public void m1(T t) {

    }
}

//定义泛型接口
public interface MyInterface<T> {
    void m1(T t);
}

//定义实现泛型接口的类
public class MyClass1<T> implements MyInterface<T> {
    @Override
    public void m1(T t) {

    }
}

//定义继承泛型类的类
public class MyClass2<T> extends MyClass1<T> {
    @Override
    public void m1(T t) {
        super.m1(t);
    }
}

public class Demo {
    public static void main(String[] args) {
        MyClass1<String> myClass1 = new MyClass1<>();
        myClass1.m1("aaa");

        MyClass2<String> myClass2 = new MyClass2<>();
        myClass2.m1("bbb");
        
        MyClass3<String> myClass3 = new MyClass3<>();
        myClass3.m1("hello");
    }
}

4.4泛型方法

  • 方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型

  • 为什么要使用泛型方法呢?因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活

格式:

[访问权限]  返回类型 <泛型> 方法名([泛型标识 参数名称]) 抛出的异常
//定义带泛型的类
public class MyClass<T> {
    public void m1(T t) {

    }
    
    //泛型方法
    public void <E> m2(E e) {

    }
	
    /*
        public int add(int a, int b) {
            System.out.println(a + "+" + b + "=" + (a + b));
            return a + b;
        }

        public float add(float a, float b) {
            System.out.println(a + "+" + b + "=" + (a + b));
            return a + b;
        }

        public double add(double a, double b) {
            System.out.println(a + "+" + b + "=" + (a + b));
            return a + b;
        }
    */
    //如果没有泛型,要实现不同类型的加法,每种类型都需要重载一个add方法;通过泛型,我们可以复用为一个方法
    public <T extends Number> double add(T a, T b) {
        System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
        return a.doubleValue() + b.doubleValue();
    }
}

public class Demo {
    public static void main(String[] args) {
        MyClass1<String> myClass1 = new MyClass1<>();
        myClass1.m1("aaa");

        Person p = new Person();
        p.setId("001");
        p.setName("zs");
        p.setAge(20);
		
        //调用泛型方法
        myClass.m2(p);
        //调用被static修饰的泛型方法
        System.out.println(test(123));
    }
    
    //static的泛型方法
    public static <T> T test(T t) {
        return t;
    }   
}

4.5通配符

<?>表示不确定的数据类型

<? extends 类型>使用时指定的类型必须是继承某个类,或者实现某个接口,即<=

<? super 类型>使用时指定的类型不能小于操作的类,即>=

举例:

  • <? extends Person> 定义泛型上边界(无穷小 ,Person]只允许泛型为PersonPerson子类的引用调用
    doubleValue()));
    return a.doubleValue() + b.doubleValue();
}

}

public class Demo {
public static void main(String[] args) {
MyClass1 myClass1 = new MyClass1<>();
myClass1.m1(“aaa”);

    Person p = new Person();
    p.setId("001");
    p.setName("zs");
    p.setAge(20);
  
    //调用泛型方法
    myClass.m2(p);
    //调用被static修饰的泛型方法
    System.out.println(test(123));
}

//static的泛型方法
public static <T> T test(T t) {
    return t;
}   

}

4.5通配符

<?>表示不确定的数据类型

<? extends 类型>使用时指定的类型必须是继承某个类,或者实现某个接口,即<=

<? super 类型>使用时指定的类型不能小于操作的类,即>=

举例:

  • <? extends Person> 定义泛型上边界(无穷小 ,Person]只允许泛型为PersonPerson子类的引用调用
  • <? super Person>定义泛型下边界[Person, 无穷大)只允许泛型为PersonPerson父类的引用调用
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值