• JavaSE 第十章 集合


    11.1 Java集合框架概述

    • 一方面, 面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象 的操作,就要对对象进行存储。另一方面,使用Array存储对象方面具有一些弊 端,而Java 集合就像一种容器,可以动态地把多个对象的引用放入容器中。
    • 数组在内存存储方面的特点:
      • 数组初始化以后,长度就确定了。
      • 数组声明的类型,就决定了进行元素初始化时的类型
    • 数组在存储数据方面的弊端:
      • 数组初始化以后,长度就不可变了,不便于扩展
      • 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。 同时无法直接获取存储元素的个数
      • 数组存储的数据是有序的、可以重复的。---->存储数据的特点单一
    • Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的 关联数组。

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

    • Collection接口:单列数据,定义了存取一组对象的方法的集合
      • List:元素有序、可重复的集合
      • Set:元素无序、不可重复的集合
    • Map接口:双列数据,保存具有映射关系“key-value对”的集合

    在这里插入图片描述

    11.2 Collection接口

    • Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法 既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
    • JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List) 实现。
    • 在 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都 当成 Object 类型处理;从 JDK 5.0 增加了泛型以后,Java 集合可以记住容 器中对象的数据类型。

    11.2.1 Collection的对象创建

    • 实际上由于Collection是一个接口,并不能创建对象,只能创建其实现类对象
    • 创建对象:
    Collection<E> col = new 具体实现类<E>() ;    // 这里用到的是多态的性质
    
    Collection<String> 集合名 = new ArrayList<>();
    Collection<Student> 集合名 = new HashSet<>();
    Collection 集合名 = new ArrayList();
    
    • 1
    • 2
    • 3
    • 4
    • 5

    : 泛型
    作用 : 用来约束集合类元素的数据类型
    使用方法 : 创建集合的时候,拿具体的引用数据类型类名替换E即可
    注意 :
    1. 泛型只能是引用数据类型不可以是基本数据类型
    2. 如果不写泛型,泛型默认是Object
    3. 等号右边的泛型可以省略,默认和前面一致

    11.2.2 Collection接口的方法

    • 添加
      add(Object obj) : 添加一个元素
    • 获取有效元素的个数
      int size()
    • 清空集合
      void clear()
    • 是否是空集合
      boolean isEmpty()
    • 删除
      boolean remove(Object obj) :通过元素的equals方法判断是否是 要删除的那个元素。只会删除找到的第一个元素
    • 转成对象数组
      Object[] toArray()
    • 遍历
      iterator():返回迭代器对象,用于集合遍历
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collection;
    
    public class Demo1 {
        public static void main(String[] args) {
            Collection<String> col = new ArrayList<>();
    
            // add()添加方法
            col.add("张三") ;
            col.add("李四") ;
            col.add("王五") ;
            col.add("张三") ;
            System.out.println("col = " + col);
            // size()元素个数
            System.out.println("col.size() = " + col.size());
            // isEmpty()是否为空
            System.out.println("col.isEmpty() = " + col.isEmpty());
    
            col.remove("张三");
            System.out.println("col = " + col);
    
            Object[] array = col.toArray();
            System.out.println("Arrays.toString(array) = " + Arrays.toString(array));
    
            col.clear();
    
            System.out.println("col = " + col);
        }
    }
    
    • 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

    11.2.3 遍历集合

    1. 转数组 Object[] toArray()
        @Test
        public void test1() {
            Collection<String> col = new ArrayList<>();
    
            col.add("张三") ;
            col.add("李四") ;
            col.add("王五") ;
            col.add("张三") ;
    
            Object[] array = col.toArray();
            for (int i = 0; i < array.length; i++) {
                System.out.println("array[" + i + "] = " + array[i]);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 迭代器对象 Iterator iterator()

    Iterator : 迭代器
    如何获取迭代器对象 : Iterator 对象名 = 集合对象.iterator();
    成员方法 :
    boolean hasNext() : 查看集合中是否有下一个元素
    E next() : 获取集合中的下一个元素

        @Test
        public void test2() {
            Collection<String> col = new ArrayList<>();
    
            col.add("张三") ;
            col.add("李四") ;
            col.add("王五") ;
            col.add("张三") ;
    
            Iterator<String> iterator = col.iterator();
            while (iterator.hasNext()) {
                System.out.println("iterator.next() = " + iterator.next());
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 增强for循环(foreach)

    增强for循环的底层还是迭代器

    格式 :

    for(容器元素类型 变量名 : 集合名/数组名){
           	//操作变量名 就是在操作集合中的元素
        }
    
    • 1
    • 2
    • 3
        @Test
        public void test3() {
            Collection<String> col = new ArrayList<>();
    
            col.add("张三") ;
            col.add("李四") ;
            col.add("王五") ;
            col.add("张三") ;
    
            for (String s : col) {
                System.out.println("s = " + s);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    11.3 Iterator迭代器

    11.3.1 ConcurrentModificationException并发修改异常

    • 问题引出:
      当我们在使用集合时,很容易遇到在遍历的过程中对其进行添加、修改、删除的操作

    示例:创建一个集合对象,在其中添加四个字符串对象,使用Iterator迭代器遍历,当遍历到元素"李四"时,向list集合中添加一个字符串对象"田七"

    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class Demo6 {
        public static void main(String[] args) {
            ArrayList<String> list = new ArrayList<>();
    
            list.add("张三") ;
            list.add("李四") ;
            list.add("王五") ;
            list.add("赵六") ;
    
            Iterator<String> iterator = list.iterator();
    
            while (iterator.hasNext()) {
                String name = iterator.next();
                System.out.println("name = " + name);
                if (name.equals("李四")) {
                    list.add("田七") ;
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    name = 张三
    name = 李四
    Exception in thread "main" java.util.ConcurrentModificationException
    	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    	at java.util.ArrayList$Itr.next(ArrayList.java:851)
    	at cn.pdsu.edu._list.Demo6.main(Demo6.java:18)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    想要了解为什么会出现
    ConcurrentModificationException这个异常,就要先了解迭代器的底层是如何对集合对象进行遍历的

    11.3.2 迭代器底层原理

    在这里插入图片描述

    知道了迭代器在遍历集合时是不能修改集合内元素的,那么增强for循环也不能,因为增强for循环底层也是用迭代器实现的。

    如何解决这一问题呢?
    最好的解决方案就是不使用,使用for循环

    import java.util.ArrayList;
    
    public class Demo6 {
        public static void main(String[] args) {
            ArrayList<String> list = new ArrayList<>();
    
            list.add("张三") ;
            list.add("李四") ;
            list.add("王五") ;
            list.add("赵六") ;
    
            for (int i = 0; i < list.size(); i++) {
                System.out.println("list.get(i) = " + list.get(i));
                if (list.get(i).equals("李四")) {
                    list.add("田七") ;
                }
            }
            System.out.println("list = " + list);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    结果:

    list.get(i) = 张三
    list.get(i) = 李四
    list.get(i) = 王五
    list.get(i) = 赵六
    list.get(i) = 田七
    list = [张三, 李四, 王五, 赵六, 田七]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    11.4 List接口

    11.4.1 List接口的方法

    List<E>接口 : 元素可重复,元素存取有序,元素有索引的单列集合根节点
    
    对象创建 :
    	//以多态的方式创建
    	List<E> 集合名 = new List的实现类集合<>();
    
    增删改查四类操作
    	增 : 
    		boolean add(E e) : 依次添加元素,永远返回true
    		
    		void add(int index, E element) : 在指定的索引位置插入元素 //插队:
    		boolean remove(Object obj) : 按照元素值进行删除,返回删除是否成功
    		void clear() : 清空集合中的所有元素
    		
    		E remove(int index) : 按照传入的索引值删除元素,返回是被删除的元素
    	改 : 
    		E set(int index,E e) : 修改指定索引位置上的元素,并返回被修改的元素
    	查 : 
    		E get(int index) : 获取集合中的指定索引元素
    		int size() : 获取集合的长度和元素的个数
    		
    		boolean contains(Object obj) : 判断集合中是否包含传入的元素
    		boolean isEmpty() : 判断集合是否为空集合
    		
    		//如果不存在,返回-1
    		int indexOf(Object o) : 查询o元素第一次出现在集合的索引位置
    		int lastIndexOf(Object o)  :查询o元素最后一次出现在集合的索引位置
    
    • 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

    遍历方式 :

    1. 转数组 Object[] toArray()
    2. 迭代器 Iterator iterator()
    3. 增强for 集合名.for
    4. 普通for 集合名.fori
    5. 列表迭代器 ListIterator listIterator()

    11.4.2 ArrayList存储自定义对象类型,五种遍历方式

    • 自定义Student类
    import java.util.Objects;
    
    public class Student {
        private String name ;
        private int age ;
    
        public Student() {
        }
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Student student = (Student) o;
            return age == student.age &&
                    Objects.equals(name, student.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", 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
    • 50
    • 51
    • 52
    • 五种遍历集合的方式
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Iterator;
    import java.util.ListIterator;
    
    public class Demo3 {
        public static void main(String[] args) {
            List<Student> list = new ArrayList<>();
    
            list.add(new Student("张三" , 20)) ;
            list.add(new Student("李四" , 21)) ;
            list.add(new Student("王五" , 22)) ;
    
            // 五种遍历集合的方式
            // 转数组遍历
            Object[] array = list.toArray();
            for (int i = 0; i < array.length; i++) {
                System.out.println("array[i] = " + array[i]);
            }
    
            // 迭代器
            Iterator<Student> iterator = list.iterator();
            while (iterator.hasNext()) {
                System.out.println("iterator.next() = " + iterator.next());
            }
    
            // 增强for循环
            for (Student student : list) {
                System.out.println("student = " + student);
            }
    
            // for循环
            for (int i = 0; i < list.size(); i++) {
                System.out.println("list.get(i) = " + list.get(i));
            }
    
            // listIterator迭代器
            ListIterator<Student> lit = list.listIterator();
            while (lit.hasNext()) {
                System.out.println("lit.next() = " + lit.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

    11.4.3 关于ArrayList集合中删除多个元素

    public class Demo4 {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
    
            list.add("张三") ;
            list.add("张四") ;
            list.add("张五") ;
            list.add("赵四") ;
            list.add("王五") ;
            list.add("李力") ;
    
            // 倒序删除
            for (int i = list.size() - 1; i >= 0; i--) {
                if (list.get(i).startsWith("张")) {
                    list.remove(i) ;
                }
            }
            // list = [赵四, 王五, 李力]
            System.out.println("list = " + list);
        }
    
        public static void method2(ArrayList<String> list) {
            for (int i = 0; i < list.size(); i++) {
                if (list.get(i).startsWith("张")) {
                    list.remove(i) ;
                    i -- ;  // 集合是一个长度变化的容器,删除成功后,list.size()长度减一
                }
            }
            // list = [赵四, 王五, 李力]
            System.out.println("list = " + list);
        }
    
        public static void method1(ArrayList<String> list) {
            for (int i = 0; i < list.size(); i++) {
                if (list.get(i).startsWith("张")) {
                    list.remove(list.get(i)) ;
                    list.remove(i) ;
                }
            }
            // list = [张四, 赵四, 王五, 李力]
            System.out.println("list = " + list);
        }
    }
    
    • 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

    11.4.4 关于ArrayList集合中Integer类型元素的删除

    public class Demo5 {
        public static void main(String[] args) {
            List<Integer> list = new ArrayList<>();
    
            list.add(1) ;
            list.add(2) ;
            list.add(3) ;
            list.add(4) ;
            list.add(5) ;
    
            int num = 2 ;
    
            // 按索引删除    删除的是索引为2位置的元素
            list.remove(num) ;
    
            // 按元素值删除   删除的是元素值为2的元素
            list.remove(Integer.valueOf(num)) ;
    
            System.out.println("list = " + list);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 这里是因为Java中调用方法的最佳适配,集合中的remove()方法是一个重载的方法,boolean remove(Object obj) E remove(int index) ,当我们传入int类型的num时,系统会自动匹配到按照索引删除的方法,可以直接使用num来进行方法的执行,这样是最方便的。如果匹配到按照元素删除的方法,int类型的num需要先进行自动装箱的操作,将int类型转为Integer类型,然后再执行remove()方法。反过来也是这样,传入的是Integer类型,就会优先匹配boolean remove(Object obj)方法

    11.4.5 ListIterator列表迭代器

    ListIterator<E>列表迭代器 : List集合下特有的迭代器对象
    	ListIterator<E>列表迭代器接口 是 Iterator<E>接口的子接口,功能更为强大.但是它属于List专属
    	
    创建列表迭代器对象的方式 :
    	1. List集合名.listIterator();
    	2. List集合名.listIterator(索引);  指定列表迭代器对象的开始位置
    	
    方法 :
    	boolean hasNext() : 判断指针右边是否有下一个元素
        E next() : (先校验镜像和原集合的内容是否一致)获取指针右边的元素并移动指针到下一个可停留位置
        void add(E e)  : 列表迭代器添加元素的方法 (先往镜像中添加元素,立刻同步回原集合)
        void set(E e)  : 修改指针指向的那个索引元素(先修改镜像,再同步到原集合)   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    使用这个迭代器也能避免并发修改异常问题

    11.4.6 ArrayList集合

    ArrayList<E>集合 数据结构是数组结构的List集合的实现;  -> 最常用的元素可重复的单列集合
    	//集合命名 : 数据类型 + 集合类型
    	
    对象创建 :
    	ArrayList<E> list = new ArrayList<>();
    
    增删改查四类操作
    	增 : 
    		boolean add(E e) : 依次添加元素,永远返回true
    		
    		void add(int index, E element) : 在指定的索引位置插入元素 //插队:
    		boolean remove(Object obj) : 按照元素值进行删除,返回删除是否成功
    		void clear() : 清空集合中的所有元素
    		
    		E remove(int index) : 按照传入的索引值删除元素,返回是被删除的元素
    	改 : 
    		E set(int index,E e) : 修改指定索引位置上的元素,并返回被修改的元素
    	查 : 
    		E get(int index) : 获取集合中的指定索引元素
    		int size() : 获取集合的长度和元素的个数
    		
    		boolean contains(Object obj) : 判断集合中是否包含传入的元素
    		boolean isEmpty() : 判断集合是否为空集合
    		
    		//如果不存在,返回-1
    		int indexOf(Object o) : 查询o元素第一次出现在集合的索引位置
    		int lastIndexOf(Object o)  :查询o元素最后一次出现在集合的索引位置
    		
    遍历方式 :
    	1. 转数组 Object[] toArray()
    	2. 迭代器 Iterator iterator()
    	3. 增强for 集合名.for -> 不修改集合内容时需要遍历推荐增强for
    	4. 普通for 集合名.fori -> 推荐
    	5. 列表迭代器  ListIterator listIterator()	
    
    • 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

    方法同List的方法相同

    11.4.7 ArrayList的底层扩容原理

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

    - 结论:
    1、当创建一个ArrayList集合时,底层会先创建一个长度为0Object数组
    2、当第一次向集合中添加元素时,源码会创建一个长度为10的新数组替换长度为0的旧数组(第一次扩容)
    3、当集合添加到第十一个元素时,容积不够,源码会再创建一个长度为15的新数组替换长度为10的老数组(第二次扩容)
    4、每当集合底层中的数组元素存满了,源码就会创建一个长度为 (老数组长度 * 1.5) 长度的新数组
        newCapacity = oldCapacity + (oldCapacity >> 1)
    5hugeCapacity()方法的源码 :
    			(minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;  
    		a 如果此时集合底层数组的长度+1 没有超过 Integer.MAX_VALUE - 8 , 那么扩容后的集合底层数组长度是MAX_ARRAY_SIZE
    		b 如果此时集合底层数组的长度+1 超过 Integer.MAX_VALUE - 8 , 那么扩容后的集合底层数组长度是 Integer.MAX_VALUE
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    当向ArrayList集合中添加第1024个元素值时,此时集合数组的长度为1234。(前提是使用无参构造创建的集合对象)

    在这里插入图片描述

    • ArrayList的JDK1.8之前与之后的实现区别?
      • JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
      • JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元 素时再创建一个始容量为10的数组

    11.4.8 LinkedList集合

    LinkedList<E>集合 : 底层数据结构是双向链表结构
    	源码验证 : 
            class Node<E> {
                E item;
                Node<E> next;
                Node<E> prev;
    
                Node(Node<E> prev, E element, Node<E> next) {
                    this.item = element;
                    this.next = next;
                    this.prev = prev;
                }
            }
    
    创建对象的格式,增删改查,遍历方式 和 ArrayList 一致 //链表结构是没有索引的,但是LinkedList集合有索引
    对象创建 :
    	LinkedList<E> list = new LinkedList<>();
    
    增删改查四类操作
    	增 : 
    		boolean add(E e) : 依次添加元素,永远返回true
    		
    		void add(int index, E element) : 在指定的索引位置插入元素 //插队:
    		boolean remove(Object obj) : 按照元素值进行删除,返回删除是否成功
    		void clear() : 清空集合中的所有元素
    		
    		E remove(int index) : 按照传入的索引值删除元素,返回是被删除的元素
    	改 : 
    		E set(int index,E e) : 修改指定索引位置上的元素,并返回被修改的元素
    	查 : 
    		E get(int index) : 获取集合中的指定索引元素
    		int size() : 获取集合的长度和元素的个数
    		
    		boolean contains(Object obj) : 判断集合中是否包含传入的元素
    		boolean isEmpty() : 判断集合是否为空集合
    		
    		//如果不存在,返回-1
    		int indexOf(Object o) : 查询o元素第一次出现在集合的索引位置
    		int lastIndexOf(Object o)  :查询o元素最后一次出现在集合的索引位置
    		
    遍历方式 :
    	1. 转数组 Object[] toArray()
    	2. 迭代器 Iterator iterator()
    	3. 增强for 集合名.for -> 不修改集合内容时需要遍历推荐增强for
    	4. 普通for 集合名.fori -> 推荐
    	5. 列表迭代器  ListIterator listIterator()	
            
    LinkedList的特殊方法 : 一些关于头尾操作的方法
    	 void addFirst(E e)  
         void addLast(E e) 
         E getFirst()  
         E getLast() 
         E removeFirst()  
         E removeLast()  
    
    • 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

    方法示例:

    import java.util.LinkedList;
    
    public class Demo8 {
        public static void main(String[] args) {
            LinkedList<String> linkedList = new LinkedList<>() ;
    
            // LinkedList的add()方法
            linkedList.add("张三") ;
            linkedList.add("李四") ;
            linkedList.add("王五") ;
    
            System.out.println("linkedList = " + linkedList);   // linkedList = [张三, 李四, 王五]
            
            // addFirst()方法,头插
            linkedList.addFirst("我是头");
            // addLast()方法,尾插
            linkedList.addLast("我是尾");
            System.out.println("linkedList = " + linkedList);   // linkedList = [我是头, 张三, 李四, 王五, 我是尾]
    
            // getFirst()得到头元素
            System.out.println("linkedList.getFirst() = " + linkedList.getFirst()); // linkedList.getFirst() = 我是头
            // getLast()得到尾元素
            System.out.println("linkedList.getLast() = " + linkedList.getLast());   // linkedList.getLast() = 我是尾
    
            // removeFirst()删除头
            System.out.println("linkedList.removeFirst() = " + linkedList.removeFirst());   // linkedList.removeFirst() = 我是头
            // 删除尾
            System.out.println("linkedList.removeLast() = " + linkedList.removeLast());     // linkedList.removeLast() = 我是尾
            
            System.out.println("linkedList = " + linkedList);   // linkedList = [张三, 李四, 王五]
        }
    }
    
    • 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
    • 对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高

    11.4.9 Vector集合

    • Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList 相同,区别之处在于Vector是线程安全的。

    • 在各种list中,最好把ArrayList作为首选。当插入、删除频繁时, 使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。

    • 新增方法:
      void addElement(Object obj)
      void insertElementAt(Object obj,int index)
      void setElementAt(Object obj,int index)
      void removeElement(Object obj)
      void removeAllElements()

    11.4.10ArrayList/LinkedList/Vector的异同

    请问ArrayList/LinkedList/Vector的异同?谈谈你的理解?ArrayList底层 是什么?扩容机制?Vector和ArrayList的最大区别?

    • ArrayList和LinkedList的异同 二者都线程不安全,相对线程安全的Vector,执行效率高。 此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于随机访问get和set,ArrayList绝对优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。

    • ArrayList和Vector的区别Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用 ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。

    11.5 Set接口

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

    • Set接口的特点 : 元素唯一,元素存取无序。Set集合是元素无索引的单列集合根节点

    • Set集合的使用

    因为Set是一个接口类型,无法直接创建对象,需要利用多态的性质。

    创建对象:Set set = new 具体实现类<>();

    增 :
    boolean add(E e) : 往集合中添加元素,返回添加是否成功
    删 :
    boolean remove(Object o) : 按照传入的元素删除集合中的该元素
    void clear() : 清空集合中所有的内容

    改 : 因为没有索引,所以没有set方法
    查 : 因为没有索引,所以没有get方法

    int size(): 集合元素个数/集合的长度

    boolean contains(Object o) : 是否包含
    boolean isEmpty() : 是否为空

    遍历
    1. 转数组
    2. 迭代器
    1. 增强for循环

    示例代码:

    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Set;
    
    public class Demo2 {
        public static void main(String[] args) {
    //        创建对象
    //        Set 集合名 = new 具体的实现类对象<>();
            Set<String> set = new HashSet<>() ;
            System.out.println("set = " + set);
    
    //        增删改查四类功能
    //        增 :
    //        boolean add(E e) : 往集合中添加元素,返回添加是否成功
            boolean b = set.add("张三");
            set.add("李四") ;
            set.add("王五") ;
            set.add(null) ;
            boolean b1 = set.add("张三");
    
            System.out.println("b = " + b);
            System.out.println("b1 = " + b1);
            System.out.println("set = " + set);
    
    //        删 :
    //        boolean remove(Object o)  : 按照传入的元素删除集合中的该元素
            boolean b2 = set.remove("王五");
            System.out.println("b2 = " + b2);
            System.out.println("set = " + set);
    
    //        改 : 因为没有索引,所以没有set方法
    
    //        查 : 因为没有索引,所以没有get方法
    //        int size() : 集合元素个数/集合的长度
            int size = set.size();
            System.out.println("size = " + size);
    //
    //        boolean contains(Object o)  : 是否包含
            boolean b3 = set.contains("张三");
            System.out.println("b3 = " + b3);
    
    //        boolean isEmpty()  : 是否为空
            boolean b4 = set.isEmpty();
            System.out.println("b4 = " + b4);
    
    //      遍历
    //        1. 转数组
            System.out.println("-----------------------");
            Object[] array = set.toArray();
            for (int i = 0; i < array.length; i++) {
                System.out.println("array[i] = " + array[i]);
            }
    
    //        2. 迭代器
            System.out.println("-----------------------");
            Iterator<String> iterator = set.iterator();
            while (iterator.hasNext()) {
                String next = iterator.next();
                System.out.println("next = " + next);
            }
    
    //        3. 增强for循环
            System.out.println("-----------------------");
            for (String s : set) {
                System.out.println("s = " + s);
            }
    
    //        void clear() : 清空集合中所有的内容
            set.clear();
            System.out.println("set = " + set);
        }
    }
    
    • 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

    set = []
    b = true
    b1 = false
    set = [null, 李四, 张三, 王五]
    b2 = true
    set = [null, 李四, 张三]
    size = 3
    b3 = true
    b4 = false
    -----------------------
    array[i] = null
    array[i] = 李四
    array[i] = 张三
    -----------------------
    next = null
    next = 李四
    next = 张三
    -----------------------
    s = null
    s = 李四
    s = 张三
    set = []

    11.5.2 Set实现类之一:HashSet集合

    • HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
    • HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除 性能。
    • HashSet 具有以下特点:
      • 不能保证元素的排列顺序
      • HashSet 不是线程安全的
      • 集合元素可以是 null
    • HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相 等,并且两个对象的 equals() 方法返回值也相等。
    • 对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。

    创建对象
    HashSet 集合名 = new HashSet<>();
    增删改查四类功能
    增 :
    boolean add(E e) : 往集合中添加元素,返回添加是否成功
    删 :
    boolean remove(Object o) : 按照传入的元素删除集合中的该元素
    void clear() : 清空集合中所有的内容
    改 : 因为没有索引,所以没有set方法
    查 : 因为没有索引,所以没有get方法
    int size() : 集合元素个数/集合的长度

    boolean contains(Object o) : 是否包含
    boolean isEmpty() : 是否为空
    遍历

    1. 转数组
    2. 迭代器
    3. 增强for循环

    -向HashSet集合中添加元素的过程:

    • 当向HashSet集合中添加元素时,HashSet集合会调用该对象的HashCode()方法来得到它的HashCode值,然后根据HashCode值再通过某种散列函数决定该元素在HashSet底层数组中的存储位置(这个散列函数会与底层数组的长度相计算得到在 数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布, 该散列函数设计的越好)
    • 如果两个元素的HashCode值相等,会再去调用equals()方法,如果equals()方法的结果为true,则添加失败;如果equals()结果为false,则会保存该元素。该数组的位置已经有元素了, 那么会通过链表的方式继续链接。
    • 哈希表:

    在这里插入图片描述

    • 重写 hashCode() 方法的基本原则
      • 在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
      • 当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等。
      • 对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
    • 重写 equals() 方法的基本原则
      • 当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是 要改写hashCode(),根据一个类的equals方法(改写后),两个截然不 同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法, 它们仅仅是两个对象。
      • 因此,违反了“相等的对象必须具有相等的散列码”。
      • 结论:复写equals方法的时候一般都需要同时复写hashCode方法。通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。

    11.5.3 Set实现类之二:LinkedHashSet

    • LinkedHashSet 是 HashSet 的子类
    • LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置, 但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
    • LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全 部元素时有很好的性能。
    • LinkedHashSet 不允许集合元素重复
    import java.util.Iterator;
    import java.util.LinkedHashSet;
    
    public class Demo3 {
        public static void main(String[] args) {
    //      创建对象
            LinkedHashSet<String> set = new LinkedHashSet<>() ;
            System.out.println("set = " + set);
    
    //      增删改查四类功能
    //      增 :
    //      boolean add(E e) : 往集合中添加元素,返回添加是否成功
            boolean b = set.add("张三");
            set.add("李四") ;
            set.add("王五") ;
            set.add(null) ;
            boolean b1 = set.add("张三");
    
            System.out.println("b = " + b);
            System.out.println("b1 = " + b1);
            System.out.println("set = " + set);
    
    //      删 :
    //      boolean remove(Object o)  : 按照传入的元素删除集合中的该元素
            boolean b2 = set.remove("王五");
            System.out.println("b2 = " + b2);
            System.out.println("set = " + set);
    
    //      改 : 因为没有索引,所以没有set方法
    
    //      查 : 因为没有索引,所以没有get方法
    //      int size() : 集合元素个数/集合的长度
            int size = set.size();
            System.out.println("size = " + size);
    
    //      boolean contains(Object o)  : 是否包含
            boolean b3 = set.contains("张三");
            System.out.println("b3 = " + b3);
    
    //      boolean isEmpty()  : 是否为空
            boolean b4 = set.isEmpty();
            System.out.println("b4 = " + b4);
    
    //      遍历
    //      1. 转数组
            System.out.println("-----------------------");
            Object[] array = set.toArray();
            for (int i = 0; i < array.length; i++) {
                System.out.println("array[i] = " + array[i]);
            }
    
    //      2. 迭代器
            System.out.println("-----------------------");
            Iterator<String> iterator = set.iterator();
            while (iterator.hasNext()) {
                String next = iterator.next();
                System.out.println("next = " + next);
            }
    
    //      3. 增强for循环
            System.out.println("-----------------------");
            for (String s : set) {
                System.out.println("s = " + s);
            }
    
    //      void clear() : 清空集合中所有的内容
            set.clear();
            System.out.println("set = " + set);
        }
    }
    
    • 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

    set = []
    b = true
    b1 = false
    set = [张三, 李四, 王五, null]
    b2 = true
    set = [张三, 李四, null]
    size = 3
    b3 = true
    b4 = false
    -----------------------
    array[i] = 张三
    array[i] = 李四
    array[i] = null
    -----------------------
    next = 张三
    next = 李四
    next = null
    -----------------------
    s = 张三
    s = 李四
    s = null
    set = []

    在这里插入图片描述

    11.5.4 Set实现类之三:TreeSet

    11.5.4.1 树结构

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

    在这里插入图片描述

    11.5.4.2 TreeSet集合
    • TreeSet集合 : 底层数据结构是红黑树结构的Set集合
    • 特点 : 元素唯一,元素存取无序,无索引,元素不能为null

    创建对象/增删改查/遍历 和 Set 接口中的功能一致的!

    • TreeSet集合可以对元素进行排序,但是排序的规则由程序员提供

    常用类型的默认排序规则:
    Integer : 默认升序 (由源码提供)
    String : 默认升序(字符串中字符的ASCII码值的升序 ) //越靠前的字符ASCII值的优先级越高 (“10” 会排在在 “2” 字符串的前面)

    自定义类型的元素存储在TreeSet集合中的排序规则 : 如果元素的类型没有提供排序规则,那么直接报错 (添加不进去)

    import java.util.TreeSet;
    
    public class Demo4 {
        public static void main(String[] args) {
    
            TreeSet<String> set = new TreeSet<>() ;
            System.out.println("set = " + set);
    
    
            set.add("王五") ;
            set.add("张三");
            set.add("李四") ;
            set.add("张三");
    
            System.out.println("set = " + set);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    set = []
    set = [张三, 李四, 王五]

    public class Demo4 {
        public static void main(String[] args) {
    
            TreeSet<Integer> set = new TreeSet<>() ;
            System.out.println("set = " + set);
    
    
            set.add(55) ;
            set.add(100);
            set.add(3) ;
            set.add(10);
            set.add(80);
    
            System.out.println("set = " + set);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    set = []
    set = [3, 10, 55, 80, 100]

    自定义Student类,没有实现Comparable接口

    public class Demo4 {
        public static void main(String[] args) {
    
            TreeSet<Student> set = new TreeSet<>() ;
    
            set.add(new Student("张三" , 19)) ;
            set.add(new Student("李四" , 20));
            set.add(new Student("王五" , 21)) ;
    
            System.out.println("set = " + set);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Exception in thread “main” java.lang.ClassCastException: cn.pdsu.edu._set.Student cannot be cast to java.lang.Comparable
    at java.util.TreeMap.compare(TreeMap.java:1294)
    at java.util.TreeMap.put(TreeMap.java:538)
    at java.util.TreeSet.add(TreeSet.java:255)
    at cn.pdsu.edu._set.Demo4.main(Demo4.java:12)

    11.5.5 比较器

    11.5.5.1 Comparable比较器
    • 当我们向 TreeSet 中添加元素时,只有第一个元素无须调用compareTo()方法进行比较,后面添加的所有元素都会调用compareTo()方法进行比较。

    • 因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同 一个类的对象。

    • 对于 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj) 方法比较返回值。

    • 当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与 compareTo(Object obj) 方法有一致的结果:如果两个对象通过 equals() 方法比较返回 true,则通过 compareTo(Object obj) 方法比较应返回0。

    对于Integer和String类,它们都有实现Comparable接口,都重写了compareto方法,所以在我们使用TreeSet集合存储Integer或String类型的数据时,它们都会进行一个排序从小到大的排序(升序)。

    import java.util.TreeSet;
    
    public class Demo2 {
        public static void main(String[] args) {
            // 创建TreeSet集合存储Integer/String类型的元素,查看其存储的方式
            TreeSet<Integer> treeSet = new TreeSet<>();
    
            // 乱序添加数据
            treeSet.add(5) ;
            treeSet.add(2) ;
            treeSet.add(4) ;
            treeSet.add(1) ;
            treeSet.add(3) ;
    
            // 输出集合查看集合内元素的数据
            // treeSet = [1, 2, 3, 4, 5]
            // 对于Integer类型的数据,在TreeSet中就是根据数值的大小进行排序的
            System.out.println("treeSet = " + treeSet);
    
    
            TreeSet<String> treeSet1 = new TreeSet<>() ;
    
            // 添加String类型的数据
            treeSet1.add("123") ;
            treeSet1.add("ABC") ;
            treeSet1.add("abc") ;
            treeSet1.add("wer") ;
            treeSet1.add("888") ;
    
            // 输出集合内数据查看排序后的顺序
            // treeSet1 = [123, 888, ABC, abc, wer]
            // 对于字符串类型的数据,在TreeSet中是按照Ascall码表进行排序的,
            // 对于中文ascall表中虽然没有,但是在其他的编码中也有中文对应的编码值
            System.out.println("treeSet1 = " + treeSet1);
        }
    }
    
    • 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
    • 对于没有实现Comparable接口的类来说,是没法将元素存入进TreeSet集合的。

    自定义Student类

    import java.util.Objects;
    
    public class Student {
        private String name ;
        private int age ;
    
        public Student() {
        }
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
        
        /** 省略set/get方法*/
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Student student = (Student) o;
            return age == student.age &&
                    Objects.equals(name, student.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", 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

    尝试将自定义类Student对象作为元素存入

    import java.util.TreeSet;
    
    public class Demo {
        public static void main(String[] args) {
            // 创建TreeSer集合
            TreeSet<Student> set = new TreeSet<>();
    
            // 向集合中存入元素
            set.add(new Student("张三" , 19)) ;
            set.add(new Student("李四" , 20)) ;
            set.add(new Student("王五" , 21)) ;
    
            System.out.println("set = " + set);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Exception in thread “main” java.lang.ClassCastException: cn.pdsu.edu._test.Student cannot be cast to java.lang.Comparable
    at java.util.TreeMap.compare(TreeMap.java:1294)
    at java.util.TreeMap.put(TreeMap.java:538)
    at java.util.TreeSet.add(TreeSet.java:255)
    at cn.pdsu.edu._test.Demo.main(Demo.java:11)

    因为Student类没有实现Comparable接口,在进行类型转换时就直接报错了,所以会有一个java.lang.ClassCastException

    • Student类实现Comparable接口并重写compareTo()方法
    import java.util.Objects;
    
    public class Student implements Comparable<Student>{
        
    	
    	......
    
    
        /**
         * 实现Comparable接口中的compareTo()方法
         * @param o     这里的o表示的是集合中存在的老元素,调用compareTo()方法的元素是新元素
         * @return  返回一个int类型的整数,0表示相等,正数表示新元素大于老元素,负数表示新元素小于老元素
         */
        @Override
        public int compareTo(Student o) {
            // 先按年龄进行比较,this - o表示升序
            if (this.age - o.age != 0) return this.age - o.age ;
            // 当年龄相等时按照字符串的排序规则返回
            return  this.name.compareTo(o.name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    import java.util.TreeSet;
    
    public class Demo {
        public static void main(String[] args) {
            // 创建TreeSer集合
            TreeSet<Student> set = new TreeSet<>();
    
            // 向集合中存入元素
            set.add(new Student("张三" , 19)) ;
            set.add(new Student("李四" , 20)) ;
            set.add(new Student("王五" , 21)) ;
    
            // set = [Student{name='张三', age=19}, Student{name='李四', age=20}, Student{name='王五', age=21}]
            // 年龄升序
            System.out.println("set = " + set);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    11.5.5.2 Comparator比较器
    • 对于Comparable比较器来说,我们需要实现Comparable接口并重写其抽象方法,类与接口之间有较强的耦合性。且对于Integer类和String类来说,它们的排序规则是无法改变的,因为无法继承这两个类对其compareTo()方法进行重写,所以有些不灵活。这时候我们就可以考虑使用Comparator比较器来对对象元素编写一套排序规则。

    • Comparator比较器 : 独立存在的比较器,给容器提供排序规则. 容器内的元素按照容器给的排序规则进行排序Comparator比较器的优先级比Comparable比较器高;

    修改Integer和String的排序规则

    import java.util.Comparator;
    import java.util.TreeSet;
    
    public class Demo1 {
        public static void main(String[] args) {
    
            // 使用Comparator比较器匿名内部类,实现compare()方法,对Integer类型的元素进行降序排序
    
            TreeSet<Integer> treeSet = new TreeSet<>(new Comparator<Integer>(){
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o2 - o1;
                }
            });
    
            treeSet.add(5) ;
            treeSet.add(2) ;
            treeSet.add(4) ;
            treeSet.add(1) ;
            treeSet.add(3) ;
    
            // treeSet = [5, 4, 3, 2, 1] 降序排列
            System.out.println("treeSet = " + treeSet);
    
            TreeSet<String> set = new TreeSet<>(new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return o2.compareTo(o1);
                }
            }) ;
    
            set.add("123") ;
            set.add("ABC") ;
            set.add("abc") ;
            set.add("wer") ;
            set.add("888") ;
    
            // set = [wer, abc, ABC, 888, 123] 降序排列
            System.out.println("set = " + set);
        }
    }
    
    
    • 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

    Student的排序规则

    import java.util.Comparator;
    import java.util.TreeSet;
    
    public class Demo {
        public static void main(String[] args) {
            // 创建TreeSer集合
            TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {
    
                @Override
                public int compare(Student o1, Student o2) {
                    return o2.getAge() - o1.getAge() ;
                }
            });
    
            // 向集合中存入元素
            set.add(new Student("张三" , 19)) ;
            set.add(new Student("李四" , 20)) ;
            set.add(new Student("王五" , 21)) ;
    
            // set = [Student{name='王五', age=21}, Student{name='李四', age=20}, Student{name='张三', age=19}]
            // 年龄降序
            System.out.println("set = " + set);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    11.5.6 Set集合中的两个注意点

    import java.util.HashSet;
    
    public class Demo5 {
        public static void main(String[] args) {
            HashSet<Person> set = new HashSet<>();
            /**
             * 在HashSet集合中存储数据时,是按照hash值来确定位置的,在存储p1时,是按照  1001,“AA”来计算hash值的
             * 而在下面对其内容进行了修改,当调用remove()方法删除p1元素时,需要先计算hash值,而此时hash值的计算是按照1001,“CC”来的
             * 但是并不能找到以1001,“AA”计算的hash值,所以删除失败,p1仍存在
             *
             * 而在存储下面的  1001,“CC”  时,hash值是按1001,“CC”计算的,虽然存在相同的元素内容,但是根本不会调用到equals()方法
             * 存储成功,而存储   1001,“AA”时,equals()方法比较结果为false,存储成功
             */
            Person p1 = new Person(1001,"AA");
            Person p2 = new Person(1002,"BB");
            set.add(p1);
            set.add(p2);
            p1.name = "CC";
            boolean remove = set.remove(p1);
    //        System.out.println("remove = " + remove); // false
            System.out.println(set);
            set.add(new Person(1001,"CC"));
            System.out.println(set);
            set.add(new Person(1001,"AA"));
            System.out.println(set);
    
            /**
             * [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
             * [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
             * [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}, Person{id=1001, name='AA'}]
             */
        }
    }
    
    • 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
    去重

    使用Set集合对list集合中的元素进行去重

    import java.util.ArrayList;
    import java.util.HashSet;
    
    public class Demo6 {
        public static void main(String[] args) {
            // 去除List集合中的重复数值
            ArrayList<Integer> list = new ArrayList<>();
    
            list.add(5) ;
            list.add(5) ;
            list.add(1) ;
            list.add(1) ;
            list.add(2) ;
    
            HashSet<Integer> hashSet = new HashSet<>();
            hashSet.addAll(list);
            ArrayList<Integer> list1 = new ArrayList<>();
    
            list1.addAll(hashSet) ;
            System.out.println("list1 = " + list1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    list1 = [1, 2, 5]

    11.6 Map接口

    11.6.1 Map接口继承树

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

    11.6.2 Map接口概述
    • Map与Collection并列存在。用于保存具有映射关系的数据:key-value

    • Map 中的 key 和 value 都可以是任何引用类型的数据

    • Map 中的 key 用Set来存放,不允许重复,即同一个 Map 对象所对应 的类,须重写hashCode()和equals()方法 - 常用String类作为Map的“键”

    • key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到 唯一的、确定的 value

    • Map接口的常用实现类:HashMap、TreeMap、LinkedHashMapProperties。其中,HashMap是 Map 接口使用频率最高的实现类

    在这里插入图片描述

    • 添加、删除、修改操作:
      • Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
      • void putAll(Map m):将m中的所有key-value对存放到当前map中
      • Object remove(Object key):移除指定key的key-value对,并返回value
      • void clear():清空当前map中的所有数据
    • 元素查询的操作:
      • Object get(Object key):获取指定key对应的value
      • boolean containsKey(Object key):是否包含指定的key
      • boolean containsValue(Object value):是否包含指定的value
      • int size():返回map中key-value对的个数
      • boolean isEmpty():判断当前map是否为空
      • boolean equals(Object obj):判断当前map和参数对象obj是否相等
    • 元视图操作的方法:
      • Set keySet():返回所有key构成的Set集合
      • Collection values():返回所有value构成的Collection集合
      • Set entrySet():返回所有key-value对构成的Set集合
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    public class Demo2 {
        public static void main(String[] args) {
    
            // 创建Map集合的实现类对象
            Map<String , String> map = new HashMap<>() ;
    
            System.out.println("map = " + map);
            System.out.println("----------------------");
    
            // 添加元素
            map.put("key1" , "value1") ;
            map.put("key2" , "value2") ;
            map.put("key3" , "value3") ;
            map.put("key4" , "value4") ;
            map.put("key5" , "value5") ;
    
            System.out.println("map = " + map);
            System.out.println("----------------------");
    
            // 修改元素
            // 修改元素和添加元素都是使用的put()方法
            map.put("key1" , "修改后") ;
            map.put("key3" , "修改后") ;
    
            System.out.println("map = " + map);
            System.out.println("----------------------");
    
            // 查找元素
            String key1 = map.get("key1");
            String key2 = map.get("key2");
    
            System.out.println("key1 = " + key1);
            System.out.println("key2 = " + key2);
            System.out.println("----------------------");
    
            // 删除元素
            map.remove("key1") ;
            map.remove("key3") ;
    
            System.out.println("map = " + map);
            System.out.println("----------------------");
    
            // 查找Map集合中是否存在指定的key/value
            boolean b = map.containsKey("key2");
            boolean b1 = map.containsValue("value2");
    
            System.out.println("b = " + b);
            System.out.println("b1 = " + b1);
            System.out.println("----------------------");
    
            // 判断当前map集合是否为空
            boolean empty = map.isEmpty();
            System.out.println("empty = " + empty);
            System.out.println("----------------------");
    
            // 返回当前map集合中key-value的个数
            int size = map.size();
            System.out.println("size = " + size);
            System.out.println("----------------------");
    
            // 返回所有key构成的set集合
            Set<String> set1 = map.keySet();
            System.out.println("set1 = " + set1);
            System.out.println("----------------------");
    
            // 返回所有value构成的set集合
            Collection<String> set2 = map.values();
            System.out.println("set2 = " + set2);
            System.out.println("----------------------");
    
            // 返回所有key-value对构成的Set集合
            Set<Map.Entry<String, String>> entries = map.entrySet();
            System.out.println("entries = " + entries);
    
        }
    }
    
    
    • 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

    map = {}
    ----------------------
    map = {key1=value1, key2=value2, key5=value5, key3=value3, key4=value4}
    ----------------------
    map = {key1=修改后, key2=value2, key5=value5, key3=修改后, key4=value4}
    ----------------------
    key1 = 修改后
    key2 = value2
    ----------------------
    map = {key2=value2, key5=value5, key4=value4}
    ----------------------
    b = true
    b1 = true
    ----------------------
    empty = false
    ----------------------
    size = 3
    ----------------------
    set1 = [key2, key5, key4]
    ----------------------
    set2 = [value2, value5, value4]
    ----------------------
    entries = [key2=value2, key5=value5, key4=value4]

    11.6.3 Map实现类之一:HashMap

    11.6.3.1 HashMap的特点
    • HashMap是 Map 接口使用频率最高的实现类。
    • 允许使用null键和null值,与HashSet一样,不保证映射的顺序。
    • 所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写: equals()和hashCode()
    • 所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类 要重写:equals()
    • 一个key-value构成一个entry
    • 所有的entry构成的集合是Set:无序的、不可重复的
    • HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true, hashCode 值也相等。
    • HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。
    11.6.3.2 HashMap的存储结构
    • JDK 7及以前版本:HashMap是数组+链表结构(即为链地址法)
    • JDK 8版本发布以后:HashMap是数组+链表+红黑树实现。

    在这里插入图片描述

    11.6.3.3 HashMap集合的方法

    HashMap实现了Map接口的方法

    • 创建对象
      • HashMap<键集元素类型,值集元素类型> 集合名 = new HashMap<>();
    • 增删改查
      • 增 / 改 :

        • V put(K key,V value) : 添加一对映射关系,添加成功返回null; 添加失败返回oldValue
      • 删 :

        • V remove(K key) : 根据传入的键删除集合中的这一对映射关系,并返回这对映射关系的值
        • boolean remove(K key,V value) : 根据传入的键和值删除集合中的一对元素,返回删除是否成功
        • void clear() : 清空集合中所有的映射关系
      • 查 :

        • V get(K key) : 根据传入的键获取此键对应的值 ;

        • int size() : 返回集合中有几对元素

        • boolean containsKey(K key) : 判断是否存在传入的键

        • boolean containsValue(V value) : 判断是否存在传入的值

        • Set keySet() : 获取双列集合的键集

        • Collection values() : 获取双列集合的值集

    • 遍历
      • Set> entrySet() :获取到HashMap集合中的所有Entry对象,从Entry对象中得到key和value值de
      • 通过keySet()方法先获取Map集合中的所有key集,然后根据key来获取value
    public class Demo3 {
        public static void main(String[] args) {
            // 创建HashMap对象
            Hashtable<String, String> map = new Hashtable<>();
    
            // 添加元素
            map.put("张三" , "男") ;
            map.put("李四" , "男") ;
            map.put("王五" , "女") ;
            map.put("赵六" , "女") ;
    
            System.out.println("map = " + map);
            System.out.println("==========================");
    
            // 修改元素
            map.put("张三" , "女") ;
    
            System.out.println("map = " + map);
            System.out.println("==========================");
    
            // 删除元素
            map.remove("张三") ;
    
            System.out.println("map = " + map);
            System.out.println("==========================");
    
            // 查找元素
            String s = map.get("李四");
    
            System.out.println("s = " + s);
            System.out.println("==========================");
    
            // 返回集合中存在几对元素
            int size = map.size();
    
            System.out.println("size = " + size);
            System.out.println("==========================");
    
            // 判断key/value是否在集合中存在
            boolean b = map.containsKey("李四");
            boolean b1 = map.containsValue("女");
    
            System.out.println("b = " + b);
            System.out.println("b1 = " + b1);
            System.out.println("==========================");
    
            // 获取Map集合中的键集
            Set<String> keys = map.keySet();
            System.out.println("keys = " + keys);
    
            System.out.println("==========================");
    
            // 获取Map集合中的值集
            Collection<String> values = map.values();
            System.out.println("values = " + values);
        }
    }
    
    • 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

    map = {赵六=女, 王五=女, 张三=男, 李四=男}
    ==========================
    map = {赵六=女, 王五=女, 张三=女, 李四=男}
    ==========================
    map = {赵六=女, 王五=女, 李四=男}
    ==========================
    s = 男
    ==========================
    size = 3
    ==========================
    b = true
    b1 = true
    ==========================
    keys = [赵六, 王五, 李四]
    ==========================
    values = [女, 女, 男]

    • 遍历Map集合内的元素
    import java.util.Hashtable;
    import java.util.Map;
    import java.util.Set;
    
    public class Demo4 {
        public static void main(String[] args) {
            // 创建HashMap对象
            Hashtable<String, String> map = new Hashtable<>();
    
            // 添加元素
            map.put("张三" , "男") ;
            map.put("李四" , "男") ;
            map.put("王五" , "女") ;
            map.put("赵六" , "女") ;
    
            // 遍历Map集合的元素
            // 第一种方法:先获取Map集合中的键集,根据键集来获取值集,遍历集合
            Set<String> keySet = map.keySet();
            for (String key : keySet) {
                String value = map.get(key);
                System.out.println(key + "-----" + value);
            }
    
            System.out.println("-----------------");
    
            // 第二种方法:获取所有的Entry对象,从entry对象中获取key/value值
            Set<Map.Entry<String, String>> entries = map.entrySet();
            for (Map.Entry<String, String> entry : entries) {
                String key = entry.getKey();
                String value = entry.getValue();
    
                System.out.println(key + "-----" + value);
            }
    
            /**
             * 赵六-----女
             * 王五-----女
             * 张三-----男
             * 李四-----男
             * -----------------
             * 赵六-----女
             * 王五-----女
             * 张三-----男
             * 李四-----男
             */
        }
    }
    
    • 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

    11.6.4 Map实现类之二:LinkedHashMap

    `- LinkedHashMap 是 HashMap 的子类

    • 在HashMap存储结构的基础上,使用了一对双向链表来记录添加 元素的顺序
    • 与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代 顺序:迭代顺序与 Key-Value 对的插入顺序一致
      `

    11.6.5 Map实现类之三:TreeMap

    • TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序。 TreeMap 可以保证所有的 Key-Value 对处于有序状态。
    • TreeSet底层使用红黑树结构存储数据
    • TreeMap 的 Key 的排序:
      • 自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有 的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
      • 定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口
    • TreeMap判断两个key相等的标准:两个key通过compareTo()方法或 者compare()方法返回0。

    11.6.6 Map实现类之四:Hashtable

    • Hashtable是个古老的 Map 实现类,JDK1.0就提供了。不同于HashMap, Hashtable是线程安全的。
    • Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询 速度快,很多情况下可以互用。
    • 与HashMap不同,Hashtable 不允许使用 null 作为 key 和 value
    • 与HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序
    • Hashtable判断两个key相等、两个value相等的标准,与HashMap一致。

    11.6.7 Map实现类之五:Properties

    • Properties 类是 Hashtable 的子类,该对象用于处理属性文件
    • 由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key 和 value 都是字符串类型
    • 存取数据时,建议使用setProperty(String key,String value)方法和 getProperty(String key)方法

    11.7 Collections工具类

    • Collections 是一个操作 Set、List 和 Map 等集合的工具类
    • Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作, 还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
    • 排序操作:(均为static方法)
      • reverse(List):反转 List 中元素的顺序
      • shuffle(List):对 List 集合元素进行随机排序
      • sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
      • sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
      • swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    
    public class Demo1 {
        public static void main(String[] args) {
            ArrayList<String> arrayList = new ArrayList<>();
    
            arrayList.add("张三") ;
            arrayList.add("李四") ;
            arrayList.add("王五") ;
            arrayList.add("赵六") ;
            arrayList.add("田七") ;
    
            // arrayList = [张三, 李四, 王五, 赵六, 田七]
            System.out.println("arrayList = " + arrayList);
    
            // 调用Collections工具类中的方法操作ArrayList集合
    
            // 集合反转
            Collections.reverse(arrayList);
            // arrayList = [田七, 赵六, 王五, 李四, 张三]
            System.out.println("arrayList = " + arrayList);
    
            // 对ArrayList集合元素进行随机排序
            Collections.shuffle(arrayList);
            // arrayList = [赵六, 田七, 李四, 王五, 张三]
            System.out.println("arrayList = " + arrayList);
    
            // 对ArrayList集合中的元素进行排序(默认排序规则)
            Collections.sort(arrayList);
            // arrayList = [张三, 李四, 王五, 田七, 赵六]
            System.out.println("arrayList = " + arrayList);
    
            // 对ArrayList集合中的元素进行排序(自定义排序规则排序规则)
            Collections.sort(arrayList, new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return o2.compareTo(o1);
                }
            });
            // arrayList = [赵六, 田七, 王五, 李四, 张三]
            System.out.println("arrayList = " + arrayList);
    
            // 交换指定位置的元素
            Collections.swap(arrayList , 0 , arrayList.size() - 1);
            // arrayList = [张三, 田七, 王五, 李四, 赵六]
            System.out.println("arrayList = " + arrayList);
        }
    }
    
    
    • 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

    Collections常用方法

    • 查找、替换
      • Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
      • Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回 给定集合中的最大元素
      • Object min(Collection) Object min(Collection,Comparator)
      • int frequency(Collection,Object):返回指定集合中指定元素的出现次数
      • void copy(List dest,List src):将src中的内容复制到dest中
      • boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
    • Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集 合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

    在这里插入图片描述

  • 相关阅读:
    Spring中Bean的作用域
    【Mac】破解死循环,成功安装 Homebrew、curl、wget,快速配置 zsh
    R语言ggplot2可视化:使用patchwork包(直接使用加号+)将两个ggplot2可视化结果横向组合起来形成单个可视化结果图
    016 Spring Boot + Vue 图书管理系统
    CPT-PLGA/FITC/Bodipy/Biotin聚乳酸共聚物/荧光素/生物素/Bodipy系列染料修饰顺铂的制备
    技术分享 | 接口自动化测试如何进行认证?
    黑磷量子点/无机荧光量子点/石墨烯量子点水凝胶/量子点/纳米水凝胶荧光探针的研究制备
    题目0099-计算堆栈中的剩余数字
    MySQL 数据库 - 通用语法 DDL DML DQL DCL
    基于Ant Design设计语言的WinForm UI界面库
  • 原文地址:https://blog.csdn.net/weixin_45890113/article/details/126414192