Java学习笔记Day12 集合

集合

面向对象对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储。另一方面,使用Array存储对象方面具有一些弊端,而Java集合就像是一种容器,可以动态的把多个对象的引用放入容器中。

数组在内存存储方面的特点

        数组初始化后,长度就确定了。

        数组声明的类型,就决定了进行元素初始化时的类型。

数组在内存存储方面的弊端

        数组初始化以后,长度不可变,不便于扩展。

        数组提供的属性和方法少,不便于进行添加,删除,插入等操作,且效率不高。同时无法直接获取存储元素的个数。

        数组存储的数据是有序的,可以重复的。

Java集合类可以用于存储数量不等的多个对象,还可以用于保存具有映射关系的关联数组。

Java集合可分为Collection和Map两种体系

Collection接口:单列数据,定义了存取一组对象的方法的集合

        List:元素有序,可重复的集合  "动态数组"

        Set:元素无序,不可重复的集合

Map接口:双列数据,保存具有映射关系“ key-value对 ”的集合

Collection接口继承树

Collection接口的常用方法

public class CollectionTest {
    @Test
    public void test() {
        Collection coll = new ArrayList();

        //将元素添加到动态数组中     要求传入的obj需要重写equals方法。
        coll.add("Aa");
        coll.add(123);//自动装箱
        coll.add(LocalDateTime.now());
        coll.add(new int[5]);

        //获取添加的元素个数
        System.out.println(coll.size());

        //将另一个集合的元素都添加到当前集合中
        Collection coll1 = new ArrayList();
        coll1.add("dasd");
        coll1.add(123456);
        coll.addAll(coll1);
        System.out.println(coll.size());
        System.out.println(coll);

        //清空集合元素
        coll.clear();

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

        //判断当前集合是否包含传入的obj
        System.out.println(coll1.contains(123456));
        //比较的是值   如果是测试是否包含自定义类的对象,需要重写类的equals方法
        System.out.println(coll1.contains(new String("dasd")));

        //判断当前集合是否包含传入的所有obj
        Collection coll2 = new ArrayList();
        coll2.add(123456);
        coll2.add("dasd");
        coll2.add(123456);
        //coll2.add(new Date());
        System.out.println(coll1.containsAll(coll2));

        //移除集合中某个元素
        coll2.remove(123456);
        System.out.println(coll2);

        //移除集合与指定集合的交集   相当于求差集
        Collection coll3 = Arrays.asList(123456, "dasd", "hh", 20.0, 111);
        coll2.removeAll(coll3);
        System.out.println(coll2);

        //求交集
        //Arrays.asList返回的是Arrays的内部类ArrayList和直接new出来不一样
        //所以用它调用Collection的方法会报错
        //Collection coll4 = Arrays.asList(123456, 5.0, "hh");
        Collection coll4 = new ArrayList();
        coll4.add(5.0);
        coll4.add("hh");
        coll4.add(123456);
        coll4.retainAll(coll3);
        System.out.println(coll4);

        //判断集合是否相等
        Collection coll5 = new ArrayList();
        coll5.add("hh");
        coll5.add(123456);
        System.out.println(coll5.equals(coll4));

        //当前对象的哈希值
        System.out.println(coll5.hashCode());

        //集合 -> 数组
        Object[] arr = coll5.toArray();
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "\t");
        }

        //扩展 数组 -> 集合   可变形参相当于Object[]
        List<Object> list = Arrays.asList("123456", 123, 123.0);
        System.out.println(list.getClass());//不是实现Collection接口的ArrayList
    }
}

Iterator接口

Iterator对象称为迭代器(设计模式的一种),主要用于遍历Collection集合中的元素。

GOF给迭代器模式的定义为:提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。

Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,所有实现Collection接口的集合类都有一个iterator方法,用以返回一个实现了Iterator接口的对象,每次返回都是新的

public class CollectionTest {
    @Test
    public void test() {
        Collection coll = new ArrayList();
        coll.add("Aa");
        coll.add(123);
        coll.add("bb");
        coll.add(123.123);
        coll.add('z');
        coll.add(123);

        //迭代器接口
        Iterator iterator = coll.iterator();

        //取数据
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());

        //普通for
//        for (int i = 0; i < coll.size(); i++) {
//            System.out.println(iterator.next());
//        }

        //hasNext()
//        while (iterator.hasNext()) {
//            System.out.println(iterator.next());
//        }

        //remove()  删除集合中指定元素如果有的话(只能在next后调用一次)
//        while (iterator.hasNext()) {
//            Object obj = iterator.next();
//            if ("bb".equals(obj)) {
//                iterator.remove();
//            }
//        }
        //遍历
//        Iterator iterator1 = coll.iterator();
//        while (iterator1.hasNext()) {
//            System.out.println(iterator1.next());
//        }

        //foreach循环遍历   增强for
        for (Object o : coll) {
            System.out.print(o + "\t");
        }
    }
}

迭代器的执行原理

Collection子接口:List接口

List集合类中元素有序,且可重复。  "动态数组"

常用实现类有ArrayList,LinkedList,Vector。

ArrayList,LinkedList,Vector的异同

同:三个类都实现了List接口,存储数据的特点相同:有序,可重复

不同:ArrayList:(List接口的主要实现类),线程不安全,效率高。底层使用Object[ ]存储

           LinkedList:对于频繁的插入,删除操作,此类效率比ArrayList高,底层使用双向链表存储

           Vector:(很古老的实现类)线程安全的,效率低。底层使用Object[ ]存储

ArrayList源码分析

jdk 7

类似单例模式饿汉式

capacity:容量

jdk 8

类似单例模式懒汉式,延迟数组的创建,节省内存

结论:在开发中建议使用带参数的构造器:ArrayList list = new ArrayList(int capacity);

LinkedList源码分析

                                                                                                                   ↓默认值为null↓

向LinkedList中添加时(add()),调用了linkLast方法

图中last为当前最后一个元素,如果是首次调用add方法,则为null

然后新建一个Node将last作为它的前一个元素,并将null作为它的后一个元素

将新建Node赋值给last,作为最后一个元素

判断原来的最后一个元素是否为空,如果为空则新添加的元素也为第一个元素

否则将原来的最后一个元素的下一个赋值为新建元素

添加完成LinkedList的size++。

Vector源码分析

通过构造器创建对象时,底层都创建了长度为10的数组

扩容方面默认扩容为原来的数组长度的2倍

List接口中的常用方法

public class CollectionTest {
    @Test
    public void test() {
        ArrayList arrayList = new ArrayList();
        arrayList.add(123456);
        arrayList.add("gyq");
        arrayList.add(18.0);

        ArrayList arrayList1 = new ArrayList();
        arrayList1.add(10086);
        arrayList1.add("gyq");

        //在指定索引位置插入元素
        arrayList1.add(1, 18.0);
        System.out.println(arrayList1);

        //在指定索引位置插入指定集合所有元素
        arrayList.addAll(1, arrayList1);
        System.out.println(arrayList);

        //获取指定索引的元素
        System.out.println(arrayList.get(3));

        //判断参数在集合中首次出现的索引位置 不存在返回-1
        System.out.println(arrayList.indexOf("gyq"));

        //判断参数在集合中末次出现的索引位置 不存在返回-1
        System.out.println(arrayList.lastIndexOf("gyq"));

        //移除指定索引位置的元素 并返回被删除的元素
        Object removed = arrayList.remove(1);
        System.out.println(removed);
        System.out.println(arrayList);

        //设置指定索引的元素 并返回被改变的元素
        Object changed = arrayList.set(0, "first");
        System.out.println(changed);
        System.out.println(arrayList);

        //返回当前集合范围在[开始索引,结束索引)内的子集合
        List subList = arrayList.subList(1, arrayList.size() - 1);
        System.out.println(subList);
        System.out.println(arrayList);

        //用迭代器遍历 也可以普通for 增强for
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

Collection子接口:Set接口

存储无序,不可重复的数据

Set接口的实现类有:

HashSet:作为Set接口的主要实现类,线程不安全的,可以存储null值

        LinkedHashSet:(继承与HashSet)遍历其内部数据时,可以按照添加的顺序遍历

        对于频繁的遍历操作。效率高于HashSet。

TreeSet:可确保集合元素处于排序状态(可以按照对象(类型要一致)的指定属性排序)

无序性和不可重复性的理解

无序性:不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值。

不可重复性:保证添加的元素按照equals()方法判断时,不能返回true,即相同的元素只能添加一个。(不准确,详见下方添加元素过程代码)

通过HashSet添加元素的过程理解不可重复性

public class SetTest {
    @Test
    public void test() {
        HashSet hashSet = new HashSet();
        hashSet.add(456);
        hashSet.add("aa");
        hashSet.add(new User("gyq", 18));
        hashSet.add(new Person("gyq", 18));
        hashSet.add(new User("gyq", 18));
        hashSet.add(123);
        hashSet.add(666);
        hashSet.add(666);


        hashSet.add(555.05);


        //                无序性
        //以下代码每次运行输出结果顺序相同,只是不和添加的顺序一样
        //LinkedList()的遍历结果和加入元素顺序一致
        //所以无序性不等于随机性
        for (Object o : hashSet) {
            System.out.print(o + "\t");
        }
        //              添加元素过程
        //              不可重复性
        //如果User没有重写equals()和hashCode()
        //遍历时则会有两个属性相同的User实例化对象元素

        //             不可重复性的理解

        //在添加元素时是根据hash值通过某个算法进行计算的结果确定位置的
        //先判断元素的hash值的计算结果与已有元素的hash值的计算结果是否相同

        //如果hash值的计算结果不同则直接进行添加,不会调用equals()
        //此例中User如果没重写hashCode方法则会有两个属性相同的User对象

        //如果hash值的计算结果相同则放置的位置相同
        //此时判断hash值是否相同,如果相同则调用equals方法
        //不同则以链表方式直接添加(将原来位置上的元素指向新添加的元素)
        //而此例中的属性相同的User和Person对象就是hash值相同调Person对象的equals(User对象)方法
        //如果结果为true则不添加,否则同样以链表方式添加


    }
}

class User {
    private String name;
    private int age;

    public User() {

    }

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

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

    @Override
    public boolean equals(Object o) {
        System.out.println("执行User的equals方法!");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && Objects.equals(name, user.name);
    }

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

class Person {
    private String name;
    private int age;

    public Person() {

    }

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

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

    @Override
    public boolean equals(Object o) {
        System.out.println("执行Person的equals方法!");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

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

结论:HashSet底层是数组和链表结合实现的;

          向HashSet中添加的数据,其所在类一定要重写hashCode方法和equals方法;

          这两个方法尽可能保持一致性:相同的对象具有相同的散列码(hashcode一样equals也为true)

hashCode()和equals()的重写

Object类的hashCode方法返回的是随机值。

上方代码中Person和User不重写hashCode方法则其在集合中放置位置不同会直接被添加到集合中。

Idea自动重写的hashcode方法调用的方法

 

开发中直接使用自动生成即可

TreeSet

向TreeSet中添加的数据,要求是相同类的对象

采用红黑树存储结构,特点:有序,查询速度比List快。

两种排序方式:自然排序和定制排序

自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()

定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()

自然排序

public class SetTest {
    @Test
    public void test() {
        TreeSet treeSet = new TreeSet();
        treeSet.add(123);
        treeSet.add(120);
        treeSet.add(180);
        treeSet.add(250);
        treeSet.add(999);
        treeSet.add(22);

        //遍历集合   是自然排序
        Iterator iterator = treeSet.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        //当集合元素是自定义类时
        //需要实现Comparable接口,重写ComoparaTo方法
        //ComparaTo方法需要对所有属性进行比较
        //否则会导致只比较一个属性就认为两者是同一元素不在添加。
        TreeSet treeSet1 = new TreeSet();
        treeSet1.add(new Person("GYQ", 19));
        treeSet1.add(new Person("GYQ", 19));
        treeSet1.add(new Person("ABC", 20));
        treeSet1.add(new Person("ABC", 10));
        treeSet1.add(new Person("DHJ", 99));
        treeSet1.add(new Person("BDD", 15));
        treeSet1.add(new Person("FAK", 60));
        Iterator iterator1 = treeSet1.iterator();

        while (iterator1.hasNext()) {
            System.out.println(iterator1.next());
        }
    }

    class Person implements Comparable {
        private String name;
        private int age;

        public Person() {

        }

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

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

        //按照姓名按照英文字母排,再按照年龄从小到大
        @Override
        public int compareTo(Object o) {
            if (o instanceof Person) {
                Person person = (Person) o;
                if (this.name.compareTo(person.name) == 0) {
                    return Integer.compare(this.age, person.age);
                } else {
                    return this.name.compareTo(person.name);
                }
            }
            throw new RuntimeException("输入类型不匹配");
        }
    }
}

定制排序

public class SetTest {
    @Test
    public void test() {
        //当集合元素是自定义类时
        //需要使用Comparator接口,重写Comopara方法
        Comparator comparator = new Comparator() {
            //按照年龄从大到小排序,年龄一样认为是一样的,不添加
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Person && o2 instanceof Person) {
                    Person p1 = (Person) o1;
                    Person p2 = (Person) o2;
                    return Integer.compare(p1.age, p2.age);
                }
                throw new RuntimeException("比较类型不匹配!");
            }
        };

        TreeSet treeSet1 = new TreeSet(comparator);
        treeSet1.add(new Person("GYQ", 19));
        treeSet1.add(new Person("ABC", 20));
        treeSet1.add(new Person("ABC", 10));
        treeSet1.add(new Person("SSS", 10));
        treeSet1.add(new Person("DHJ", 99));
        treeSet1.add(new Person("BDD", 15));
        treeSet1.add(new Person("FAK", 60));
        Iterator iterator1 = treeSet1.iterator();

        while (iterator1.hasNext()) {
            System.out.println(iterator1.next());
        }
    }

    class Person {
        private String name;
        private int age;

        public Person() {

        }

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

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

Map接口

Map接口继承树

HashMap:Map的主要实现类,线程不安全的,效率高,可以存储null的key和value

        LinkedHashMap:保证在遍历元素时,是按照添加时的顺序,在HashMap的基础上添加了一对指针,指向了前一个和后一个元素,对于频繁的遍历操作,效率高于HashMap。


TreeMap:保证按照添加的key-value的key对进行排序,实现排序遍历。自然排序或定制排序

底层使用红黑树


HashTable:比较古老的实现类,线程安全的,效率低,不可以存储null的key和value

        Properties:常用来处理配置文件,key和value都是String类型

Map结构的理解

Map中的key:无序的,不可重复的,使用Set存储所有的key key所在的类要重写equals()和hashCode()

Map中的value:无序的,可重复的,使用Collection存储所有的value value所在的类要重写equals()

一个键值对:key-value构成了一个Entry对象

Map中的entry:无序的,不可重复的,使用Set存储所有的entry

HashMap的底层实现原理

jdk 7

jdk 8

HashMap的源码分析

见视频

LinkedHashMap的底层实现

Map的常用方法

public class MapTest {
    @Test
    public void test() {
        Map hashMap = new HashMap();
        //添加元素
        hashMap.put(1, "gyq");
        hashMap.put(2, "gzz");
        hashMap.put(3, "uzi");
        hashMap.put(4, "clv");
        hashMap.put(5, "god");
        //key相同时覆盖前面的内容
        hashMap.put(4, "ggg");
        System.out.println(hashMap);

        //将另一个map的元素全部放入
        Map hashMap1 = new HashMap();
        hashMap1.put(0, "zzz");
        hashMap1.put(7, "hhh");
        hashMap.putAll(hashMap1);
        System.out.println(hashMap);

        //删除集合中指定key的键值对,并返回对应value,若不存在返回null
        Object removed = hashMap.remove(7);
        System.out.println(removed);

        //清空map
        //hashMap.clear();
        System.out.println(hashMap.size());

        //判断map是否为空
        System.out.println(hashMap.isEmpty());

        //查询指定key的值
        System.out.println(hashMap.get(1));

        //判断map中是否包含指定key/value
        System.out.println(hashMap.containsKey(0));
        System.out.println(hashMap.containsValue("摆烂"));

        //判断当前map和指定map是否相等
        System.out.println(hashMap.equals(hashMap1));

        //获取map中key的集合
        Set keySet = hashMap.keySet();
        //遍历map
        for (Object o : keySet) {
            System.out.println(o + "-" + hashMap.get(o));
        }

        //获取value组成的集合
        Collection values = hashMap.values();
        System.out.println(values);

        //获取所有键值对组成的集合
        Set entrySet = hashMap.entrySet();
        Iterator iterator = entrySet.iterator();
        while (iterator.hasNext()) {
            Object nextObj = iterator.next();
            Map.Entry nextEntry = (Map.Entry) nextObj;
            System.out.println(nextEntry.getKey() + "--" + nextEntry.getValue());
        }
    }
}

TreeMap两种添加方式

向TreeMap中添加数据,要求key必须是由同一个类创建的对象

因为要按照Key进行排序,自然排序,定制排序


自然排序

要求key所在类继承Comparable接口,重写CompareTo方法(决定排序方式)

public class MapTest {
    @Test
    public void test() {
        TreeMap treeMap = new TreeMap();
        treeMap.put(new User("gyq", 24), "999999");
        treeMap.put(new User("gyq", 12), "123456");
        treeMap.put(new User("asd", 12), "111111");
        treeMap.put(new User("zzz", 100), "888888");
        treeMap.put(new User("clv", 90), "777777");
        treeMap.put(new User("uzi", 99), "666666");
        treeMap.put(new User("add", 22), "541688");

        Set entrySet = treeMap.entrySet();
        for (Object o : entrySet) {
            Map.Entry entry = (Map.Entry) o;
            System.out.println(entry.getKey().toString() + "-" + entry.getValue());
        }
    }
}

class User implements Comparable {
    private String name;
    private int age;

    public User() {
    }

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }


    //先根据年龄从小到大排,再根据姓名按字母顺序排
    @Override
    public int compareTo(Object o) {
        if (o instanceof User) {
            User anotherUser = (User) o;
            if (Integer.compare(this.age, anotherUser.age) == 0) {
                return this.name.compareTo(anotherUser.name);
            } else
                return Integer.compare(this.age, anotherUser.age);
        }
        throw new RuntimeException("类型错误");
    }

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

定制排序

要使用Comparator接口

public class MapTest {
    @Test
    public void test() {
        //定制排序   按名字排,名字一样按年龄从小到大排
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                System.out.println("调用了Compare");
                if (o1 instanceof User && o2 instanceof User) {
                    User user1 = (User) o1;
                    User user2 = (User) o2;
                    if (user1.getName().compareTo(user2.getName()) == 0) {
                        return Integer.compare(user1.getAge(), user2.getAge());
                    } else return user1.getName().compareTo(user2.getName());
                }
                throw new RuntimeException("比较类型错误");
            }
        });
        treeMap.put(new User("gyq", 24), "999999");
        treeMap.put(new User("gyq", 12), "123456");
        treeMap.put(new User("asd", 12), "111111");
        treeMap.put(new User("zzz", 100), "888888");
        treeMap.put(new User("clv", 90), "777777");
        treeMap.put(new User("uzi", 99), "666666");
        treeMap.put(new User("add", 22), "541688");

        Set entrySet = treeMap.entrySet();
        for (Object o : entrySet) {
            Map.Entry entry = (Map.Entry) o;
            System.out.println(entry.getKey().toString() + "-" + entry.getValue());
        }
    }
}

class User {
    private String name;
    private int age;

    public User() {
    }

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

Properties处理属性文件

新建test.properties文件,里面内容如下

处理

public class PropertiesTest {
    @Test
    public void test() throws IOException {
        Properties prpts = new Properties();
        FileInputStream fis = new FileInputStream("test.properties");
        prpts.load(fis);
        String name = prpts.getProperty("name");
        String pwd = prpts.getProperty("password");
        System.out.println(name);
        System.out.println(pwd);
    }
}

如果出现读取乱码问题,在File -> Setting -> File Encodings中

三个选项都选为UTF-8  并将下面的对勾打上

Collections工具类

是一个操作Set,List和Map等集合的工具类

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

public class CollectionsTest {
    @Test
    public void test() {
        List list = new ArrayList();
        list.add(99);
        list.add(909);
        list.add(999);
        list.add(-900);
        list.add(888);
        System.out.println(list);

        //反转list中元素的顺序
//        Collections.reverse(list);

        //随机排序
//        Collections.shuffle(list);

        //根据元素的自然顺序对list集合元素升序排序
//        Collections.sort(list);

        //根据Comparater的指定顺序
//        Collections.sort(list, new Comparator<Integer>() {
//            @Override
//            public int compare(Integer o1, Integer o2) {
//                return -Integer.compare(o1, o2);
//            }
//        });

        //交换list的指定位置上的元素
//        Collections.swap(list, 0, 3);

        System.out.println(list);

        //获取Collection中最大值
//        System.out.println(Collections.max(list));

        //获取Collection中最大值(按照自定义排序规则)
//        Integer max = Collections.max(list, new Comparator<Integer>() {
//            @Override
//            public int compare(Integer o1, Integer o2) {
//                return -Integer.compare(o1, o2);
//            }
//        });
//        System.out.println(max);

        //获取Collection中最小值的两种方式同上

        //返回指定集合中指定元素出现的次数
//        int times = Collections.frequency(list, 888);
//        System.out.println(times);

        //将src中的内容复制到dest中  注意:dest集合的size()必须大于src的size()
//        List dest = Arrays.asList(new Object[list.size()]);
//        Collections.copy(dest, list);
//        System.out.println(dest);

        //将集合包装为线程安全的 Collection和Map接口的相关实现类都有相关方法
//        List synchronizedList = Collections.synchronizedList(list);
//        System.out.println(synchronizedList);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值