• java中的集合


    1.集合概述

    1)什么是集合?有什么用?
    数组其实就是一个集合。集合实际上就是一个容器。可以用来容纳其他类型的数据。
    集合为什么说在开发中使用较多?
    集合是一个容器,是一个载体,可以一次容纳多个对象。
    在实际开发中,假设连接数据库,数据库当中有10条记录,
    那么假设把这10条记录查询出来,在java程序中会将10条
    数据封装成10个java对象,然后将10个java对象放到某一个
    集合当中,将集合传到前端,然后遍历集合。将一个数据一个
    数据展现出来。

    2)集合中存储的是引用。
    集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,
    集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用。)
    list.add(100);//自动装箱Integer
    注意:
    集合在java中本身是一个容器,是一个对象。
    集合中任何时时候存储的都是"引用"。

    3)在java中每一个不同的集合,底层会对于不同的数据结构。往不同的集合中
    存储元素,等于将数据放到了不同的数据结构当中。什么是数据结构?数据存储的
    结构就是数据结构。不同的数据结构,数据存储方式不同。例如:
    数组,二叉树,链表,哈希表…
    以上这些都是从常见的数据结构。

    你往集合c1中放数据,可能是放到数组上了。
    你往集合c2中放数据,可能是放到二叉树上了。

    你是要不同的集合等同于使用不同的数据结构。

    new ArrayList(); 创建一个集合,底层是数组。
    new LinkedList(); 创建一个集合对象,底层是链表。
    new TreeSet(); 创建一个集合对象,底层是二叉树。

    4)集合在java JDK中哪个包下?
    java.util.*;
    所有的集合类和集合接口都在java.util包下。

    5)集合的继承结构图背会!

    6)在java中集合分为两大类:
    一类是单个方式存储元素:
    单个方式存储元素,这一类集合中超级父接口: java.util.Collection;

    一类是以键值对儿的方式存储元素:
    以键值对的方式存储元素,这一类集合中超级父接口: java.util.Map;

    2.集合继承结构图(老杜)

    在这里插入图片描述
    在这里插入图片描述

    3.集合接口中常用的方法

    package com.jmpower.javase.collection;
    
    import java.util.ArrayList;
    import java.util.Collection;
    
    /*
    关于java.util.Collection接口中常用的方法。
        1.Collection中能放什么元素?
            没有使用"泛型"之前,Collection中可以存储Object的所有子类型。
            使用了"泛型"之后,Collection中只能存储某个具体的类型。
            集合后期我们会学习"泛型"语法。目前先不用管。Collection中什么都能存,
            只要是Object的子类型就行。(集合中不能直接存储基本数据类型,也不能存
            java对象,只是存储java对象的内存地址。)
        2.Collection中的常用方法:
            1) boolean add(Object e) 向集合中添加元素
            2) int size() 获取集合中的元素个数
            3) void clear()  清空集合
            4) boolean contains(Object o)  判断当前集合中是否包含元素o,包含返回true,不包含返回false
            5) boolean remove(Object o)  删除集合中的某个元素
            6) boolean isEmpty()  判断该集合中元素的个数是否为0
            7) Object[] toArray()  调用这个方法可以把集合转换成数组【作为了解,使用不多】
     */
    public class CollectionTest01 {
        public static void main(String[] args) {
            //创建一个集合对象
            //Collection c=new Collection();//接口是抽象的,无法实例化。
    
            //多态
            Collection c=new ArrayList();
            //测试Collection接口中的常用方法:
            //1.c.add(),添加元素
            c.add(1200);//自动装箱,实际上是放进去了一个对象的内存地址。Integer x=new Integer(1200);
            c.add(3.14);//自动装箱
            c.add(new Object());
            c.add(new Student());;
            c.add(true);
    
            //2.c.size(),获取集合中元素的个数
            System.out.println("集合中的元素个数:"+c.size());// 5
    
            //3.c.clear(),清空集合
            c.clear();
            System.out.println("集合中的元素个数:"+c.size());// 0
    
            //再次向集合中添加元素
            c.add("Hello");//"Hello"对象的内存地址存到了集合当中
            c.add("World");
            c.add("浩克");
            c.add("花木兰");
    
            //4.c.contains(Object o),判断集合中是否包含某个元素
            boolean flag=c.contains("浩克");
            System.out.println(flag);//true
            boolean flag2=c.contains("浩克2");
            System.out.println(flag2);//false
    
            //当前集合中的元素个数5
            System.out.println("集合中的元素个数:"+c.size());// 5
    
            //5.c.remove(),删除集合中指定元素
            c.remove("浩克");
            System.out.println(c.contains("浩克"));//false
            System.out.println("集合中的元素个数:"+c.size());// 4
    
            //6.c.isEmpty(),判断集合是否为空
            System.out.println(c.isEmpty());// false
            c.clear();//清空集合
            System.out.println(c.isEmpty());// true
    
            //再次添加元素
            c.add("abc");
            c.add("HelloWorld!");
            c.add("efg");
            c.add(new Student());
    
            //7.c.toArray(),转换成数组(了解,使用不多)
            Object[] obj=c.toArray();
            for(int i=0;i<obj.length;i++)
            {
                System.out.print(obj[i].toString()+" ");
            }
            System.out.println();
        }
    }
    class Student{
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    4.迭代专题

    样例1(迭代器的使用)

    package com.jmpower.javase.collection;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashSet;
    import java.util.Iterator;
    
    /**
     *关于集合遍历/迭代专题(重点)
     */
    public class CollectionTest02 {
        public static void main(String[] args) {
            //注意: 以下的遍历方式/迭代方式,是所有Collection通用的一种方式。
            //在Map集合中不能用。在所有的Collection以及子类中使用。
    
            //创建集合对象
            Collection c=new ArrayList();//后面集合无所谓,主要看前面的Collection接口,怎么遍历/迭代。
            //添加元素
            c.add(100);
            c.add("abc");
            c.add("def");
            c.add(new Object());
            //对集合Collection进行遍历/迭代
            //第一步: 获取集合对象的迭代器对象Iterator
            Iterator it=c.iterator();
            //第二步: 通过以上获取的迭代器对象开始迭代/遍历集合
            /*
            以下两个方法是迭代器Iterator中的方法。
                boolean hasNext() 如果迭代具有更多元素,则返回 true 。
                Object next() 返回迭代中的下一个元素,并且索引指向下一位。
             */
            while (it.hasNext())
            {
                //这样写许
                /*Object obj=it.next();
                System.out.println(obj);*/
                //这样也许
                System.out.println(it.next());
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    样例2(迭代器是通用的)

    package com.jmpower.javase.collection;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashSet;
    import java.util.Iterator;
    
    /**
     * 关于集合的迭代/遍历
     */
    public class CollectionTest03 {
        public static void main(String[] args) {
            //创建集合对象
            Collection c1=new ArrayList();//ArrayList集合:有序可重复
            //添加元素
            c1.add(1);
            c1.add(2);
            c1.add(3);
            c1.add(4);
            c1.add(1);
    
            //迭代集合
            Iterator it=c1.iterator();
            while(it.hasNext())
            {
                //存进去是什么类型,取出来就是什么类型。
                Object obj=it.next();
                /*if(obj instanceof Integer)
                {
                    System.out.println("Integer类型:");
                }*/
                //只不过在输出的时候会转换成字符串。因为这里println会调用toString()方法。
                System.out.println(obj);
            }
    
            //HashSet集合: 无序不可重复
            Collection c2=new HashSet();
            //无序:存进去和取出来的顺序不一定相同。
            //不可重复: 存储100,不能再存储100
            c2.add(100);
            c2.add(400);
            c2.add(300);
            c2.add(100);
            Iterator it2=c2.iterator();
            while(it2.hasNext())
            {
                System.out.println(it2.next());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    5.深入Collection的conatins方法

    1)contain方法底层调用equals方法

    package com.jmpower.javase.collection;
    
    import java.util.ArrayList;
    import java.util.Collection;
    
    /*
    深入Collection集合的contains方法:
        boolean contains(Object o)
            判断集合中是否包含某个对象o
            如果包含返回true,如果不包含返回false
        contains方法是用来判断集合中是否包含某个元素的方法,
        那么他在底层是怎么判断集合中是否包含某个元素的呢?
            调用了equals方法进行比对。
            equals方法返回true,就表示包含这个元素。
     */
    public class CollectionTest04 {
        public static void main(String[] args) {
            //创建集合对象
            Collection c=new ArrayList();
    
            //向集合中存储元素
            String s1=new String("abc");
            c.add(s1);
    
            String s2=new String("def");
            c.add(s2);
    
            String s3=new String("abc");
    
            //集合的元素个数
            System.out.println("元素的个数是:"+c.size());
            System.out.println(c.contains(s3));//true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    2)测试contains方法

    package com.jmpower.javase.collection;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Objects;
    /*
    测试contains方法
    结论: 存放在集合中的类型,一定要重写equals方法。
     */
    public class CollectionTest05 {
        public static void main(String[] args) {
            //创建集合对象
            Collection c=new ArrayList();
            //创建用户对象
            User u1=new User("jack");
            //加入集合
            c.add(u1);
    
            //判断集合中是否包含u2
            User u2=new User("jack");
    
            //没有重写equals之前: 这个结果是false
            //System.out.println(c.contains(u2));// false
    
            //重写equals之后.比较的时候会比较name
            System.out.println(c.contains(u2));// true
    
            /*Integer x=new Integer(10000);
            c.add(x);
            Integer y=new Integer(10000);
            System.out.println(c.contains(y));// true*/
            //x.equals(y)--->true
        }
    }
    
    class User{
        private String name;
        public User(){}
        public User(String name){
            this.name=name;
        }
    
        //重写equals方法
        //这个equals方法比较原理是: 只要姓名一样就表示同一个用户。
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || !(o instanceof User)) return false;
            User u = (User) o;
            return u.name.equals(this.name);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    6.深入Collection的remove方法

    package com.jmpower.javase.collection;
    
    import java.util.ArrayList;
    import java.util.Collection;
    /*
    测试remove方法。
        结论: 存放在集合中的类型,一定要重写equals方法。
     */
    public class CollectionTest06 {
        public static void main(String[] args) {
            //创建集合对象
            Collection c=new ArrayList();
            //创建用户对象
            User u1=new User("jack");
            //加入集合
            c.add(u1);
            //remove之前的集合大小
            System.out.println(c.size());// 1
    
            User u2=new User("jack");
            c.remove(u2);
            //remove之后的集合大小
            System.out.println(c.size());// 0
        }
    }
    class User{
        private String name;
        public User(){}
        public User(String name){
            this.name=name;
        }
    
        //重写equals方法
        //这个equals方法比较原理是: 只要姓名一样就表示同一个用户。
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || !(o instanceof User)) return false;
            User u = (User) o;
            return u.name.equals(this.name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    7.Collection接口中的contains方法和remove方法底层都会重写equals。

    8.关于集合中元素的删除

    package com.jmpower.javase.collection;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    /*
    关于集合元素的remove。
        重点: 当集合的结构发生改变时,迭代器必须重新获取,如果还是用以前老的迭代器,会出现异常
        java.util.ConcurrentModificationException
    
        重点: 在迭代集合元素的过程中,不能调用集合对象的remove方法,删除元素,会出现异常:
        java.util.ConcurrentModificationException
    
        重点: 在迭代元素的过程当中,一定要使用迭代器Iterator的remove方法,删除元素,
        不要使用集合自带的remove方法删除元素。
     */
    public class CollectionTest07 {
        public static void main(String[] args) {
            //创建集合
            Collection c=new ArrayList();
    
            //注意: 此时获取的迭代器,指向的是那时集合中没有元素状态下的迭代器。
            //一定要注意: 集合结构只要发生改变,迭代器必须重新获取。
            //当集合结构发生了改变,迭代器没有重新获取是,会出现这个异常:
            //java.util.ConcurrentModificationException
            /*//获取迭代器
            Iterator it=c.iterator();*/
    
            //添加元素
            c.add(1);
            c.add(2);
            c.add(3);
            //获取迭代器
            Iterator it=c.iterator();
            //遍历集合
            while(it.hasNext())
            {
                //编写代码时next()方法返回值是Object
                //Integer i=it.next();
                Object obj=it.next();
    
                //在迭代集合元素的过程中,不能调用集合对象的remove方法,删除元素
                //删除元素,改变了集合的结构,下一次需要重新获取迭代器,所以出现异常:
                //java.util.ConcurrentModificationException
                //直接通过集合删除元素,没有通知迭代器。(导致迭代器的快照和原集合状态不同。)
                //出现异常根本原因是: 集合中元素删除了,但是没有通知迭代器(代器不知道集合变化了)
                //c.remove(obj);
    
                //循环中,使用迭代器来删除元素。
                //迭代器去删除时,会自动更新迭代器,并且更新集合。(删除集合中的元素)
                it.remove();//删除的一定是迭代器指向的当前元素。
                System.out.println(obj);
            }
    
            System.out.println(c.size());// 0
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    9.List接口特有方法

    package com.jmpower.javase.collection;
    
    import java.util.*;
    
    /*
    测试List接口中常用方法。
        1.List集合存储元素特点: 有序可重复
            有序: List集合中的元素有下标。从0开始,以1递增。
            可重复: 存储一个1,还可以在存储一个1.
        2.List既然是Collection接口的子接口,那么肯定List接口有自己"特色"的方法。
            以下只列出List接口特有的常用的方法:
                void add(int index, Object element) //在指定位置上添加元素
                Object set(int index, Object element) //修改指定位置的元素
                Object get(int index) //输出指定位置的元素
                int indexOf(Object o) //获取指定对象第一次出现的索引。
                int lastIndexOf(Object o) //获取指定对象最后一次出现的索引。
                Object remove(int index) //删除指定下标位置的元素
     */
    public class ListTest01 {
        public static void main(String[] args) {
            //创建List类型的集合
            //List myList=new LinkedList();
            //List myList=new Vector();
            List myList=new ArrayList();
    
            //添加元素
            myList.add("A");
            myList.add("B");
            myList.add("C");
            myList.add("C");
            myList.add("D");
    
            //在指定位置上添加元素
            myList.add(1,"King");
    
            //迭代
            Iterator it=myList.iterator();
            while(it.hasNext())
            {
                Object obj=it.next();
                System.out.println(obj);
            }
    
            System.out.println("``````````````````````````");
            //输出指定位置的元素
            System.out.println(myList.get(1));// King
    
            //获取指定对象第一次出现的索引。
            System.out.println(myList.indexOf("C"));// 3
    
            //获取指定对象最后一次出现的索引。
            System.out.println(myList.lastIndexOf("C"));// 4
    
            //删除指定下标位置的元素
            //删除下标为0的元素
            myList.remove(0);
            System.out.println(myList.size());// 5
    
            System.out.println("```````````````````````````");
            //修改指定位置的元素
            myList.set(2,"Soft");
            //遍历集合
            Iterator it2=myList.iterator();
            while(it2.hasNext())
            {
                Object obj=it2.next();
                System.out.println(obj);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    10.ArrayList集合初始化容量及扩容

    package com.jmpower.javase.collection;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /*
    ArrayList集合:
        1.默认初始化容量10
        2.集合底层是一个Object[]数组。
        3.构造方法:
            new ArrayList();
            new ArrayList(20);
        4.ArrayList集合的扩容:
            增长到原容量的1.5倍。
            ArrayList集合底层是数组,怎么优化?
                尽可能少的扩容。因为数组扩容效率比较低,建议在使用ArrayList集合
                的时候预估元素的个数,给定一个初始化容量。
        5.数组优点:
            检索效率比较高。(每个元素占用空间大小相同,内存地址是连续的,知道首元素的内存地址,
            然后知道下标,通过数学表达式计算出元素的的内存地址,所以检索效率最高。)
        6.数组缺点:
            随机增删元素效率比较低。
            另外数组无法存储大数据量。(很难找到一块巨大的连续的内存空间)
        7.向数组末尾添加元素,效率最高,不受影响。
        8.面试官经常问的一个问题?
            这么多的集合中,你用哪个集合最多?
                答:ArrayList集合。
                因为往数组末尾添加元素,效率不受影响。
                另外,我们检索/查找某个元素的操作比较多。
     */
    public class ArrayListTest01 {
        public static void main(String[] args) {
            //默认初始化容量是10
            //数组的长度是10
            List list1=new ArrayList();
            //集合的size()方法是获取当前集合的元素的个数。不是获取集合的容量。
            System.out.println(list1.size());// 0
    
            //指定初始化容量
            //数组的长度是20
            List list2=new ArrayList(20);
            //集合的size()方法是获取当前集合的元素的个数。不是获取集合的容量。
            System.out.println(list2.size());// 0
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    11.ArrayList集合的构造方法

    package com.jmpower.javase.collection;
    
    import java.util.*;
    
    /*
    集合ArrayList的构造方法。
     */
    public class ArrayListTest02 {
        public static void main(String[] args) {
            //默认初始化容量10
            List myList1=new ArrayList<>();
    
            //指定初始化容量100
            List myList2=new ArrayList<>(100);
    
            //创建一个HashSet集合
            Collection c=new HashSet();
            c.add(100);
            c.add(200);
            c.add(50);
    
            //通过这个构造方法就可以将HashSet集合转换成List集合
            List myList3=new ArrayList<>(c);
            for(int i=0;i<myList3.size();i++)
            {
                System.out.println(myList3.get(i));
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    12.LinkedList集合

    链表优点:
    由于链表上的元素在空间存储上内存地址不连续。
    所以随机增删元素的时候不会有大量的元素位移,因此随机增删效率较高。
    在以后开发中,如果遇到随即增删集合中的元素的业务比较多时,建议使用
    LinkedList。

    链表缺点:
    不能通过数学表达式计算被查找元素的内存地址。每一次查找都是从头
    结点开始遍历,直到找到为止。所以LinkeList集合检索/查找的效率
    较低。

    ArrayList: 把检索发挥到极致。(末尾添加元素的效率还是很高的)
    LinkedList: 把随机增删发挥到极致。
    加元素往往都是往末尾添加,所以ArrayList用的比LinkedList多。

    package com.jmpower.javase.collection;
    
    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;
    
    /*
    链表优点:
        由于链表上的元素在空间存储上内存地址不连续。
        所以随机增删元素的时候不会有大量的元素位移,因此随机增删效率较高。
        在以后开发中,如果遇到随即增删集合中的元素的业务比较多时,建议使用
        LinkedList。
    
    链表缺点:
        不能通过数学表达式计算被查找元素的内存地址。每一次查找都是从头
        结点开始遍历,直到找到为止。所以LinkeList集合检索/查找的效率
        较低。
    
        ArrayList: 把检索发挥到极致。(末尾添加元素的效率还是很高的)
        LinkedList: 把随机增删发挥到极致。
        加元素往往都是往末尾添加,所以ArrayList用的比LinkedList多。
     */
    public class LinkedListTest01 {
        public static void main(String[] args) {
            //ListedList集合底层也是有下标的。
            //注意: ArrayList之所以检索效率比较高,不是单纯因为下标的原因。是因为底层数组发挥的作用。
            //LinkedList集合照样有下标,但是检索/查找某个元素的时候效率比较低,因为只能从头节点开始一个一个遍历。
            List list=new LinkedList();
            list.add("a");
            list.add("b");
            list.add("c");
    
            for(int i=0;i<list.size();i++)
            {
                Object obj=list.get(i);
                System.out.println(obj);
            }
    
            //LinkedList集合有初始化容量吗?没有
            //最初这个链表没有任何元素。first和last都是null。
            //不管是LinkedList还是ArrayList,以后写代码的时候不需要关心具体是哪个集合。
            //因为我们要面向接口编程,调用的方法都是接口中的方法。
            //List list2=new ArrayList();//这样写表示底层用了数组。
            List list2=new LinkedList();//这样写表示底层用了双向链表。
    
            //以下这些方法都是面向接口编程。
            list2.add("abc");
            list2.add("def");
            list2.add("ghi");
    
            for(int i=0;i<list2.size();i++)
            {
                System.out.println(list2.get(i));
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    13.Vector集合

    Vector:
    1.底层也是一个数组.
    2.初始化容量: 10
    3.怎么扩容的?
    扩容之后是原容量的2倍。
    10–>20–>40–>80

    4.ArrayList集合扩容特点:
    ArrayList集合扩容是原容量1.5倍数。

    5.Vector中的所有方法都是线程同步的,都带有synchronized关键字。
    是线程安全的。效率比较低,使用较少了。

    6.怎么讲一个线程不安全的ArrayList集合转换成线性安全的呢?
    使用集合工具类: java.util.Collections

    java.util.Collection 是集合接口
    java.util.Collections 是集合工具类

    package com.jmpower.javase.collection;
    
    import java.util.*;
    
    /*
    Vector:
        1.底层也是一个数组.
        2.初始化容量: 10
        3.怎么扩容的?
            扩容之后是原容量的2倍。
            10-->20-->40-->80
    
        4.ArrayList集合扩容特点:
            ArrayList集合扩容是原容量1.5倍数。
    
        5.Vector中的所有方法都是线程同步的,都带有synchronized关键字。
        是线程安全的。效率比较低,使用较少了。
    
        6.怎么讲一个线程不安全的ArrayList集合转换成线性安全的呢?
            使用集合工具类: java.util.Collections
    
            java.util.Collection 是集合接口
            java.util.Collections 是集合工具类
     */
    public class VectorTest01 {
        public static void main(String[] args) {
            //创建一个vector集合
            List vector=new Vector();
            //Vector vector=new Vector();
    
            //添加元素
            //默认容量10个。
            vector.add(1);
            vector.add(2);
            vector.add(3);
            vector.add(4);
            vector.add(5);
            vector.add(6);
            vector.add(7);
            vector.add(8);
            vector.add(9);
            vector.add(10);
            //满了之后扩容(扩容之后的容量是20)
            vector.add(11);
    
            Iterator it=vector.iterator();
            while(it.hasNext())
            {
                System.out.println(it.next());
            }
    
            List myList=new ArrayList();//非线程安全的。
            //变成线程安全。
            Collections.synchronizedCollection(myList);
    
            //myList集合是线程安全的了。
            myList.add("111");
            myList.add("222");
            myList.add("333");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    14.泛型机制

    package com.jmpower.javase.collection;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    /*
    1.JDK5.0之后推出了新特性: 泛型。
    2.泛型这种语法机制,只在编译阶段起作用,只是给编译器参考的。(运行时泛型没有用)
    3.泛型的好处是什么?
        第一:集合中存储的元素类型统一了。
        第二:从集合中取出的元素类型是泛型指定的类型,不需要进行大量的"向下转型"
    4.泛型的缺点是什么?
        导致集合中的元素缺乏多样性!
     */
    public class GenericTest01 {
        public static void main(String[] args) {
            //用泛型来指定集合中存储的数据类型
            List<Animal> myList=new ArrayList<Animal>();
    
            //指定List集合中只能存储Animal,那么存储String就编译报错了。
            //这样用了泛型之后,集合中元素的数据类型更加统一了。
            //myList.add("abc");
    
            //创建对象
            Cat c=new Cat();
            Bird b=new Bird();
    
            //加入集合
            myList.add(c);
            myList.add(b);
    
            //获取迭代器
            //这个迭代器迭代的是Animal类型
            Iterator<Animal> it=myList.iterator();
            while(it.hasNext())
            {
                //使用泛型之后,每一次迭代返回的是Animal类型
                //Animal a=it。next();
                //这里不需要进行强制类型转换了。直接调用。
                //a.move();
    
                //调用子类特有的方法还是需要向下转换的!
                Animal a= it.next();
                if(a instanceof Cat)
                {
                    Cat x=(Cat) a;
                    x.catchMouse();
                }
                if(a instanceof Bird)
                {
                    Bird y=(Bird) a;
                    y.fly();
                }
            }
        }
    }
    class Animal{
        //父类自带方法
        public void move(){
            System.out.println("动物在移动!");
        }
    }
    class Cat extends Animal{
        //特有方法
        public void catchMouse(){
            System.out.println("猫捉老鼠!");
        }
    }
    class Bird extends Animal{
        //特有方法
        public void fly(){
            System.out.println("鸟儿在飞行!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    15.自动类型推断

    package com.jmpower.javase.collection;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    /*
    JDK8之后引入了: 自动类型推断。(又称为钻石表达式)
     */
    public class GenericTest02 {
        public static void main(String[] args) {
            //ArrayList<这里的类型会自动推断>(),前提是JDK8之后才允许。
            //自动类型推断,钻石表达式!
            List<Animal> myList=new ArrayList<>();
    
            myList.add(new Animal());
            myList.add(new Cat());
            myList.add(new Bird());
    
            //遍历
            Iterator<Animal> it=myList.iterator();
            while(it.hasNext())
            {
                Animal a=it.next();
                a.move();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    16.自定义泛型

    package com.jmpower.javase.collection;
    
    /*
    自定义泛型可以吗?可以
        自定义泛型的时候,<>尖括号中的是一个标识符,随便写。
        java源代码中经常出现的是:
            
        E是Element单词首字母。
        T是Type单词首字母。
     */
    public class GenericTest03<标识符随便写> {
        public void doSome(标识符随便写 o){
            System.out.println(o);
        }
        public static void main(String[] args) {
            //new对象的时候指定了泛型是: String类型
            GenericTest03<String> gt=new GenericTest03<>();
    
            //类型不匹配
            //gt.doSome(100);
    
            gt.doSome("abc");
    
            GenericTest03<Integer> gt2=new GenericTest03<>();
            gt2.doSome(100);
    
            //类型不匹配
            //gt2.doSome("123");
    
            MyIterator<String> mi=new MyIterator<>();
            String s1=mi.get();
    
            MyIterator<Animal> mi2=new MyIterator<>();
            Animal a=mi2.get();
    
            //不用泛型,就是Object类型
            //GenericTest03 gt3=new GenericTest03();
            //gt3.doSome(new Object());
        }
    }
    class MyIterator<T>{
        public T get(){
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    17.增强for循环(foreach)

    1)foreach语法

    package com.jmpower.javase.collection;
    
    /*
    JDK5.0之后推出了一个新特性: 叫做增强for循环,或者叫做foreach
     */
    public class ForEachTest01 {
        public static void main(String[] args) {
            //int类型数组
            int[] arr={1,2,3,4,5,6};
    
            //遍历数组(普通for循环)
            for(int i=0;i<arr.length;i++)
            {
                System.out.println(arr[i]);
            }
    
            //增强for(foreach)
            //以下是语法
            /*for(元素类型 变量名 : 数组或集合){
                System.out.println(变量名);
            }*/
            System.out.println("==================================");
            //foreach有一个缺点:没有下标。在需要使用下标的循环中,不建议使用增强for循环。
            for(int date:arr)
            {
                //date就是数组中的元素(数组中的每一个元素)
                System.out.println(date);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    2)集合使用forecach

    package com.jmpower.javase.collection;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    /*
    集合使用foreach
     */
    public class ForEachTest02 {
        public static void main(String[] args) {
            //创建List集合
            List<String> strList=new ArrayList<>();
    
            //添加元素
            strList.add("hello");
            strList.add("world");
            strList.add("kitty");
    
            //遍历,使用迭代器方式
            Iterator<String> it=strList.iterator();
            while(it.hasNext())
            {
                System.out.println(it.next());
            }
            System.out.println("========================");
    
            //使用下标方式(只针对有下标的集合)
            for(int i=0;i<strList.size();i++)
            {
                System.out.println(strList.get(i));
            }
            System.out.println("=========================");
    
            //使用foreach
            for(String t:strList)
            {
                System.out.println(t);
            }
            System.out.println("=========================");
    
            List<Integer> list=new ArrayList<>();
            list.add(100);
            list.add(200);
            list.add(300);
            for(Integer t:list)//t代表集合中的元素
            {
                System.out.println(t);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    18.HashSet集合特点

    package com.jmpower.javase.collection;
    
    import java.util.HashSet;
    import java.util.Set;
    
    /*
    HashSet集合:
        无序不可重复。
     */
    public class HashSetTest01 {
        public static void main(String[] args) {
            //演示一下HashSet集合特点
            Set<String> strs=new HashSet<>();
    
            //添加元素
            strs.add("hello3");
            strs.add("hello4");
            strs.add("hello1");
            strs.add("hello2");
            strs.add("hello3");
            strs.add("hello3");
            strs.add("hello3");
            strs.add("hello3");
    
            //遍历
            /*
            hello1
            hello4
            hello2
            hello3
            1. 存储时顺序和取出时顺序不同。
            2. 不可重复。
            3. 放到HahSet集合中的元素实际上是放到HahMap集合的key部分
             */
            for(String s:strs)
            {
                System.out.println(s);
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    19.演示TreeSet集合特点

    package com.jmpower.javase.collection;
    
    import java.util.Set;
    import java.util.TreeSet;
    
    /*
    TreeSet集合存储元素特点:
        1. 无序不可重复,但是存储的元素可以自动按照大小顺序排序!
        称为: 可排序集合。
    
        2.无序: 存进去的顺序和取出来的顺序不同。并且没有下标。
     */
    public class TreeSetTest01 {
        public static void main(String[] args) {
            //演示一下TreeSet集合特点
            //创建集合对象
            Set<String> strs=new TreeSet<>();
            //添加元素
            strs.add("A");
            strs.add("B");
            strs.add("C");
            strs.add("Y");
            strs.add("Z");
            strs.add("K");
            strs.add("B");
            //遍历
            /*
            A
            B
            C
            K
            Y
            Z
            从小到大自动排序!
             */
            for(String s:strs)
            {
                System.out.println(s);
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    20.Map接口常用方法

    package com.jmpower.javase.map;
    
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    
    /*
    java.util.Map接口中常用的方法:
        1.Map和Collection没有继承关系。
        2.Map集合以key和value的方式存储数据:键值对
            key和value都是引用数据类型。
            key和value都是存储对象的内存地址。
            key起到主导的地位,value是key的一个附属品。
    
        3.Map接口中常用方法:
            V put(K key, V value)    //向Map集合中添加键值对
            V get(Object key)   //通过key获取value
            void clear()    //清空Map集合
            boolean containsKey(Object key) //判断Map中是否包含某个key
            boolean containsValue(Object value) //判断Map中是否包含某个value
            boolean isEmpty()   //判断Map集合中元素个数是否为0
            V remove(Object key)    //通过key删除键值对
            int size()  //获取Map集合中键值对的个数。
            Collection values()  //获取Map集合中所有的value,返回一个Collection
    
            Set keySet() //获取Map集合所有的key(所有的键是一个Set集合)
    
            Set> entrySet()
                将Map集合转换成Set集合
                假设现在有一个Map集合,如下所示:
                    map1集合对象
                    key             value
                    -----------------------
                    1               zhangsan
                    2               lisi
                    3               wangwu
                    4               zhaoliu
    
                    Set set=map1.entrySet();
                    set集合对象
                    1=zhangsan  【注意:Mao集合通过entrySey()方法转换成的这个Set集合,Set集合中元素的类型是 Map.Entry】
                    2=lisi      【Map.Entry和String一样,都是一种类型的名字,只不过:Map.Entry是静态内部类,是Map中的静态内部类】
                    3=wangwu
                    4=zhaoliu
    
     */
    public class MapTest01 {
        public static void main(String[] args) {
            //创建Map集合对象
            Map<Integer,String> map=new HashMap<>();
            //向Map集合中添加键值对
            map.put(1,"zhangsan");
            map.put(2,"lisi");
            map.put(3,"wangwu");
            map.put(4,"zhaoliu");
            //通过key获取value
            String value=map.get(2);
            System.out.println(value);// lisi
            //判断集合中是否包含某个key
            //contains方法底层调用的都是equals进行比较的,所以自定义类需要重写equals方法
            System.out.println(map.containsKey(2));// true
            //判断集合中是否包含某个value
            System.out.println(map.containsValue("lisi"));// true
            //判断集合是否为空
            System.out.println(map.isEmpty());// false
            //获取键值对的个数
            System.out.println(map.size());// 4
            //通过key删除key-value
            map.remove(2);
            System.out.println(map.size());// 3
            //获取所有的value
            Collection<String> values=map.values();
            for(String s:values)
            {
                System.out.println(s);
            }
            //清空map集合
            map.clear();
            System.out.println(map.size());// 0
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    21.遍历Map集合

    package com.jmpower.javase.map;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    /*
    遍历Map集合
     */
    public class MapTest02 {
        public static void main(String[] args) {
            //第一种方式
            //先获取所有的key,然后通过key去遍历value
            Map<Integer,String> map=new HashMap<>();
            map.put(1,"zhangsan");
            map.put(2,"lisi");
            map.put(3,"wangwu");
            map.put(4,"zhaoliu");
            //获取所有的key
            Set<Integer> set=map.keySet();
            //1)通过迭代器的方式迭代
            /*Iterator it=set.iterator();
            while(it.hasNext())
            {
                Integer key=it.next();
                String value=map.get(key);
                System.out.println(key+"="+value);
            }*/
            //2)通过foreach方式
            for(Integer t:set)
            {
                System.out.println(t+"="+map.get(t));
            }
            System.out.println("================================");
    
            //第二种方式(这种方式效率较高,因为获取key和value都是直接从node对象中获取的属性值,适合大数据量处理)
            //Set> = map.entrySet()
            //每个set元素,相当于一个node,包含了K,V
            Set<Map.Entry<Integer,String>> set1=map.entrySet();
            //1)通过迭代器的方式迭代
            /*Iterator> it=set1.iterator();
            while(it.hasNext())
            {
                Map.Entry node=it.next();
                Integer key=node.getKey();
                String value=node.getValue();
                System.out.println(key+"="+value);
            }*/
            //2)通过foreach方式
            for(Map.Entry<Integer,String> node:set1)
            {
                System.out.println(node.getKey()+"="+node.getValue());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    22.哈希表(HashMap)数据结构

    map.put(k,v)的实现原理
    map.get(k)的实现原理
    如图所示:
    在这里插入图片描述

    package com.jmpower.javase.map;
    
    import java.util.HashMap;
    import java.util.Map;
    /*
    HashMap集合:
        1.HashMap集合底层是哈希表/散列表数据结构。
        2.哈希表是一个怎么样的数据结构呢?
            哈希表是一个数组和单向链表的结合体。
            数组: 在查询方面效率很高,随即增删方面效率很低。
            单向链表: 在随即增删方面效率较高,在查询方面效率很低。
            哈希表将以上两种数据结构融合在一起,充分发挥他们各自的优点。
        3.HashMap底层的源代码:
            public class HashMap{
    
                //Hashmap底层实际上就是一个数组。(一维数组)
                Node[] table;
    
                //静态内部类HashiMap.Node
                static class Node{
                    final int hash;//哈希值(哈希值是key的hashCode()方法的执行结果。hash值通过哈希函数/算法,可以转换成数组下标)
                    final K key;//存储Map集合的那个key
                    V value;//存储Map集合的那个value
                    Node next;//下一个节点的内存地址
                }
            }
            哈希表/散列表: 一维数组,这个数组中每一个元素是一个单向链表。(数组和链表的结合体)
        4.最主要掌握的是:
            map.put(k,v);
            v = map.get(k);
            以上这两个方法的实现原理,必须掌握!
        5.HashMap集合的key部分特点:
            无序不可重复。
            为什么无序?因为不一定挂到那个单向链表上。
            不可重复是怎么保证的?equals方法来保证HashMap集合的key不可重复。
            如果key重复了,value会覆盖。
    
            放在HashMap集合key部分的元素即使就是放到HashSet集合中了。
            所有HashSet集合中的元素也需要同时重写hashCode()和equals()方法。
    
        6.哈希表HashMap使用不当时无法发挥性能!
            假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了
            纯单向链表。这种情况我们成为: 散列分布不均匀。
            什么是散列分布均匀?
                假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,
                是散列分布均匀的。
        7.重点: 放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法。
        8.HashMap集合的默认初始化容量是16,默认加载因子是0.75
            这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。
            
            重点,记住: HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,
            这是因为达到散列均匀,为了提高HashMap集合的存储效率,所必须的。
     */
    public class HashMapTest01 {
        public static void main(String[] args) {
            //测试HashMap集合key部分的元素特点
            //Integer是key,它的hashCode和equals都重写了。
            Map<Integer,String> map=new HashMap<>();
            map.put(1111,"zhangsan");
            map.put(6666,"lisi");
            map.put(7777,"wangwu");
            map.put(2222,"zhaoliu");
            map.put(2222,"liuxing");
            map.put(7777,"wangzong");//key重复的时候value会自动覆盖
    
            System.out.println(map.size()); // 4
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    23.HashMap同时重写hashCode和equals

    package com.jmpower.javase.map;
    
    import java.util.HashSet;
    import java.util.Objects;
    import java.util.Set;
    
    /*
    1.向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后在调用equals方法!
    equals方法有可能调用,也有可能不调用。
        拿put(k,v)举例,什么时候equals不会调用?
            k.hashCode()方法返回哈希值,
            哈希值通过哈希算法转换成数组下标。
            数组下标位置上如果是null,equals就不会执行。
        拿get(k)举例,什么时候equals不会调用?
            k.hashCode()方法返回哈希值。
            哈希值经过哈希算法转换成数组下标。
            数组下标位置上如果是null,equals就不会执行。
    
    2.注意: 如果一个类的equals方法重写了,那么hashCode()方法必须重写。
    并且equals方法返回如果是true,hashCode()方法返回的值必须一样。
        equals方法返回true表示两个对象相同,在同一个单向链表上比较。
        那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。
        所有hashCode()方法的返回值也应该相同。
        (纠正:同一个单向链表上的哈希值不一定是相同的,但是哈希值%length是相同的)
    
    3.hashCode()和equals()方法不必研究,直接使用IDEA工具生成,但是这两个方法必须同时生成。
    
    4.终极结论:
        放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。
     */
    public class HashMapTest02 {
        public static void main(String[] args) {
            Student s1=new Student("zhangsan");
            Student s2=new Student("zhangsan");
    
            //重写equals方法之前false
            //System.out.println(s1.equals(s2)); // false
    
            //重写equals方法之后true
            System.out.println(s1.equals(s2));// true
    
            System.out.println("s1的哈希值是"+s1.hashCode());// 1324119927(重写之后-1432604525)
            System.out.println("s2的哈希值是"+s2.hashCode());// 81628611(重写之后-1432604525)
    
            //s1.equals(s2)结果已经是true了,表示s1和s2是一样的,那么往HashSet集合中放的话,
            //按说只能放进去一个。(HashSet集合特点: 无序不可重复)
            Set<Student> students=new HashSet<>();
            students.add(s1);
            students.add(s2);
            System.out.println(students.size());// 1
        }
    }
    class Student {
        String name;
    
        public Student(){}
        public Student(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Student student = (Student) o;
            return Objects.equals(name, student.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    24.HashMap和Hashtable的区别

    1)HashMap的key和value可以为null,Hashtable不可以。
    2)HashMap是非线程安全的,Hashtable是线程安全的。
    3)Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。

    package com.jmpower.javase.map;
    
    import java.util.HashMap;
    import java.util.Hashtable;
    import java.util.Map;
    //HashMap和Hashtable的区别?
    /*
    Hashtable的key可以为null吗?
        Hashtable集合的key和value都是不能为null的。
        HashMap集合的key和value都是可以为null的。
    
    Hashtable方法都带有synchronized: 线程安全的。
    线程安全有其他的方案,这个Hashtable对线程的处理
    导致效率较低,使用较少。
    
    Hashtable和HashMap一样,底层都是哈希表数据结构。
    Hashtable的初始化容量是11,默认加载因子: 0.75
    Hashtable的扩容: 原容量 * 2 + 1
     */
    public class HashtableTest01 {
        public static void main(String[] args) {
            //Hashtable的key和value不能为null
            Map map=new Hashtable();
            //java.lang.NullPointerException
            //map.put(null,"123");
            //map.put(100,null);
    
            //HashMap的key和value可以为null
            Map map1=new HashMap();
            map1.put(null,"123");
            map1.put(100,null);
            System.out.println(map1.size());// 2
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    25.属性类Properties类

    package com.jmpower.javase.map;
    
    import java.util.Properties;
    /*
    目前只需要掌握Properties属性类对象的相关方法即可。
    Properties是一个Map集合,继承Hashtable类,Properties的key和value都是String类型。
    Properties被称为属性类对象。
    Properties是线程安全的。
     */
    public class PropertiesTest01 {
        public static void main(String[] args) {
            //创建一个Properties对象
            Properties pro = new Properties();
    
            //需要掌握Properties的两个方法: 存 和 取
            pro.setProperty("address", "shandongyantai");
            pro.setProperty("name", "bb");
            pro.setProperty("phone", "155~");
            pro.setProperty("QQ", "2421503290");
    
            //通过key获取value
            String address = pro.getProperty("address");
            String name = pro.getProperty("name");
            String phone = pro.getProperty("phone");
            String QQ = pro.getProperty("QQ");
    
            System.out.println(address);
            System.out.println(name);
            System.out.println(phone);
            System.out.println(QQ);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    26.TreeSet对String是可排序的

    package com.jmpower.javase.map;
    
    import java.util.TreeSet;
    
    /*
    1.TreeSet集合底层实际上是一个TreeMap
    2.TreeSet集合底层是一个二叉树
    3.放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。
    4.TreeSet集合中的元素: 无序不可重复,但是可以按照元素的大小顺序自动排序。
    称为: 可排序集合
     */
    public class TreeSetTest01 {
        public static void main(String[] args) {
            //创建一个TreeSet集合
            TreeSet<String> ts=new TreeSet<>();
            //添加String
            ts.add("zhangsan");
            ts.add("lisi");
            ts.add("wangwu");
            ts.add("zhaoliu");
            ts.add("liuxing");
            //遍历
            /*
            lisi
            liuxing
            wangwu
            zhangsan
            zhaoliu
             */
            for(String t:ts)
            {
                System.out.println(t);
            }
            //遍历
            /*
            100
            200
            300
            400
             */
            TreeSet<Integer> ts2=new TreeSet<>();
            ts2.add(100);
            ts2.add(400);
            ts2.add(300);
            ts2.add(200);
            for(Integer t:ts2)
            {
                System.out.println(t);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    27.TreeSet对自定义类型排序

    1)TreeSet无法直接对自定义类型排序

    package com.jmpower.javase.map;
    
    import java.util.TreeSet;
    
    /*
    对自定义的类型来说,TreeSet可以排序吗?
        以下程序中对于Person类型来说,无法排序。因为没有指定Person对象之间的比较规则。
        谁大谁小没有说明。
    
        以下程序运行的时候出现了这个异常:
            java.lang.ClassCastException: 
                class com.jmpower.javase.map.Person 
                cannot be cast to class java.lang.Comparable
        出现这个异常的原因是:
            Person类没有实现java.lang.Comparable接口。
     */
    public class TreeSetTest02 {
        public static void main(String[] args) {
            Person p1=new Person(32);
            Person p2=new Person(45);
            Person p3=new Person(14);
            Person p4=new Person(28);
    
            //创建TreeSet集合
            TreeSet<Person> persons=new TreeSet<>();
            //添加元素
            persons.add(p1);
            persons.add(p2);
            persons.add(p3);
            persons.add(p4);
    
            //遍历
            for(Person t:persons)
            {
                System.out.println(t);
            }
        }
    }
    class Person{
        int age;
        public Person(int age){
            this.age=age;
        }
    
        //重写toString()方法
        public String toString(){
            return "Person[age="+age+"]";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    2)实现CompareTo接口

    package com.jmpower.javase.map;
    
    import java.util.TreeSet;
    
    
    public class TreeSetTest03 {
        public static void main(String[] args) {
            Customer c1=new Customer(32);
            Customer c2=new Customer(45);
            Customer c3=new Customer(14);
            Customer c4=new Customer(28);
    
            //创建TreeSet集合
            TreeSet<Customer> customers=new TreeSet<>();
            //添加元素
            customers.add(c1);
            customers.add(c2);
            customers.add(c3);
            customers.add(c4);
    
            //遍历
            for(Customer t:customers)
            {
                System.out.println(t);
            }
        }
    }
    
    //放在TreeSet集合中的元素需要实现java.util.CompareTo接口。
    //并且实现CompareTo方法。equals可以不写。
    class Customer implements Comparable<Customer>{
        int age;
        public Customer(int age){
            this.age=age;
        }
    
        //重写toString()方法
        public String toString(){
            return "Person[age="+age+"]";
        }
    
        //需要在这个方法中编写比较的逻辑,或者说比较的规则,按照什么进行比较!
        //k.compareTo(t.key);
        //拿着参数k和集合中的每一个k进行比较,返回值可能是>0 <0 =0
        //比较规则最终还是由程序员指定的: 例如按照年龄升序。或者按照年龄降序
        @Override
        public int compareTo(Customer o) {//c1.compareTo(c2);
            //this是c1
            //o是c2
            //c1和c2比较的时候,就是this和o比较
            /*int age1=this.age;
            int age2=o.age;
            if(age1>age2){
                return 1;
            }else if(age1
            //return this.age-o.age;//按照年龄升序
            return o.age-this.age;//按照年龄降序
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    3)比较规则的写法

    package com.jmpower.javase.map;
    
    import java.util.TreeSet;
    
    /*
    先按年龄升序,如果年龄一样再按照姓名升序。
     */
    public class TreeSetTest04 {
        public static void main(String[] args) {
            TreeSet<VIP> vips =new TreeSet<VIP>();
            vips.add(new VIP(20,"b张三"));
            vips.add(new VIP(20,"a李四"));
            vips.add(new VIP(24,"王五"));
            vips.add(new VIP(18,"赵六"));
    
            //遍历
            /*
            Vip{age=18, name='赵六'}
            Vip{age=20, name='a李四'}
            Vip{age=20, name='b张三'}
            Vip{age=24, name='王五'}
             */
            for (VIP v:vips)
            {
                System.out.println(v);
            }
        }
    }
    class VIP implements Comparable<VIP>{
        int age;
        String name;
    
        public VIP(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Vip{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    
        //写排序规则
        @Override
        public int compareTo(VIP o) {
            if(this.age==o.age){
                //年龄相同时,按照名字排序。
                //姓名时String类型,可以直接比。调用compareTo来完成比较。
                return this.name.compareTo(o.name);
            }
            //年龄不相同时,按照年龄排序。
            return this.age-o.age;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    4)实现比较器Comparator接口

    package com.jmpower.javase.map;
    
    import java.util.Comparator;
    import java.util.TreeSet;
    
    /*
    TreeSet集合中元素可以排序的第二种方式: 使用比较器方式。
    最终结论:
        放到TreeSet或者TreeMap集合key部分的元素要想做到排序,包括两种方式:
            第一种,放在集合中的元素实现java.lang.Comparable接口。
            第二种,在构造TreeSet或者TreeMap集合的时候给他传一个比较器对象。
    Comparable和Comparator怎么选择?
        当比较规则不会发生改变的时候,或者或比较规则只有1个的时候,建议实现Comparable接口。
        如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。
    
        Comparator接口设计符合OCP原则。
     */
    public class TreeSetTest05 {
        public static void main(String[] args) {
            //创建TreeSet集合的时候,需要使用比较器
            //TreeSet wuGuis=new TreeSet<>();//这样写不行,没有通过构造方法传递一个比较器进去。
    
            //给构造方法传一个比较器
            //TreeSet wuGuis=new TreeSet<>(new WuguiComparator());
    
            //还可以使用匿名内部类的方式(这个类没有名字。直接new接口。)
            TreeSet<WuGui> wuGuis=new TreeSet<>(new Comparator<WuGui>() {
                @Override
                public int compare(WuGui o1, WuGui o2) {
                    return o1.age-o2.age;
                }
            });
            
            wuGuis.add(new WuGui(10000));
            wuGuis.add(new WuGui(5000));
            wuGuis.add(new WuGui(8000));
            wuGuis.add(new WuGui(1000));
    
            for(WuGui wuGui:wuGuis)
            {
                System.out.println(wuGui);
            }
        }
    }
    class WuGui{
        int age;
    
        public WuGui(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "WuGui{" +
                    "age=" + age +
                    '}';
        }
    }
    
    //单独在这里编写一个比较器
    //比较器实现java.util.Comparator接口。(Comparable时java.lang包下的。Comparator时Java.util包下的。)
    class WuguiComparator implements Comparator<WuGui>{
        @Override
        public int compare(WuGui o1, WuGui o2) {
            //指定比较规则
            //按照年龄升序
            return o1.age-o2.age;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    28.Collections工具类

    package com.jmpower.javase.map;
    
    import java.util.*;
    
    /*
    java.util.Collection 集合接口
    java.util.Collections 集合工具类,方便集合操作
    
    Collections.synchronizedList(集合);// 变成线程安全的
    Collections.sort(集合); // 排序,前提要保证集合实现了Comparable接口
     */
    public class CollectionsTest {
        public static void main(String[] args) {
            //ArrayList集合不是线程安全的。
            List<String> list=new ArrayList<>();
    
            //变成线程安全的
            Collections.synchronizedList(list);
    
            list.add("abe");
            list.add("abc");
            list.add("abq");
            list.add("abc");
            //排序
            Collections.sort(list);
            for(String s: list)
            {
                System.out.println(s);
            }
    
            List<WuGui2> wuGui2s=new ArrayList<>();
            wuGui2s.add(new WuGui2(5000));
            wuGui2s.add(new WuGui2(10000));
            wuGui2s.add(new WuGui2(8000));
            wuGui2s.add(new WuGui2(500));
    
            //注意: 对List集合中元素排序,需要保证List集合中的元素实现了: Comparable接口.
            Collections.sort(wuGui2s);
    
            for(WuGui2 wuGui2:wuGui2s)
            {
                System.out.println(wuGui2);
            }
    
            //对Set集合怎么排序呢?
            Set<String> set=new HashSet<>();
            set.add("king");
            set.add("wang");
            set.add("liu");
            set.add("zhang");
            List<String> myList=new ArrayList<>(set);
            Collections.sort(myList);
            for(String s:myList)
            {
                System.out.println(s);
            }
        }
    }
    class WuGui2 implements Comparable<WuGui2>{
        int age;
    
        public WuGui2(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "WuGui{" +
                    "age=" + age +
                    '}';
        }
    
        @Override
        public int compareTo(WuGui2 o) {
            return this.age-o.age;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
  • 相关阅读:
    MySQL之JDBC编程增删改查
    定制AI问答机器人前需要准备什么数据来训练AI模型?
    栈和队列你真的会用了么?
    【学习笔记】4、组合逻辑电路(下)
    【23种设计模式】中介者模式(Mediator Pattern) .Net Core实现
    学习嵌入式系统的推荐步骤:
    “探寻服务器的无限潜能:从创意项目到在线社区,你会做什么?”
    2023 ICPC 网络赛 第二场 部分题解 (待完善)
    Codeforces Global Round 23 E CF1746E Joking (Hard Version)
    一秒完成精准测温,孩子睡觉时也能用,凡米鼓膜耳温枪T6上手
  • 原文地址:https://blog.csdn.net/m0_66689823/article/details/125879980