第1章 集合高级

一、集合体系结构介绍和课程安排

1、单列集合

一次添加一个元素

  • 都是Collection接口的实现类

  • ArrayList、LinkedList实现了List接口

    • 特点:存取有序、有索引、可以存储重复的
  • TreeSet、HashSet、LinkedHashSet实现了Set接口

    • 特点:存取无序、无索引、不可以存储重复的
  • 都继承了Collection,是Collection类的子接口

2、双列集合

一次添加两个元素

  • TreeMap、HashMap、LinkedHashMap

二、Collection的使用

java 复制代码
Collection常用成员方法
public boolean add(E e) : 把给定的对象添加到当前集合中
public void clear() : 清空集合中所有的元素
public boolean isEmpty() : 判断当前集合是否为空
public boolean remove(E e) : 把给定的对象在当前集合中删除
public boolean contains(Object obj) : 判断当前集合中是否包含给定的对象
public int size() : 返回集合中元素的个数(集合的长度)
package com.itheima.collection;

方法的使用范例

java 复制代码
import com.itheima.pojo.Student;
​
import java.util.ArrayList;
import java.util.Collection;
​
public class CollectionDemo1 {
    public static void main(String[] args) {
        Collection<Student> c = new ArrayList<>();
​
        c.add(new Student("张三", 23));
        c.add(new Student("李四", 24));
        c.add(new Student("王五", 25));
​
        //判断当前集合中是否包含给定的对象  需要重写equals方法
        System.out.println(c.contains(new Student("李四", 24)));
​
        //把给定的对象在当前集合中删除     需要重写equals方法
        c.remove(new Student("李四", 24));
​
        System.out.println(c);
​
    }
​
    private static void method() {
        // 使用多态的形式创建集合对象
        Collection<String> c = new ArrayList<>();
        //把给定的对象添加到当前集合中
        c.add("张三");
        c.add("李四");
        c.add("王五");
​
        System.out.println(c);
        //判断当前集合是否为空
        System.out.println(c.isEmpty());
        System.out.println(c.size());
​
        //清空集合中所有的元素
        c.clear();
​
        System.out.println(c);
        System.out.println(c.isEmpty());
        System.out.println(c.size());
    }
}

三、集合的通用遍历方式

1、迭代器

iterator() 返回此集合中元素的迭代器

hashNext():判断集合中是否还有元素 next():取出集合元素,并将指针向后移动

java 复制代码
package com.itheima.collection;
​
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
​
public class CollectionDemo2 {
    /*
        集合通用遍历方式 - 迭代器
     */
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        c.add("张三");
        c.add("李四");
        c.add("王五");
//以下为迭代器的使用案例
        // 1. 通过集合对象, 获取迭代器
        Iterator<String> it = c.iterator();     // new Itr();
        // 2. 循环判断是否还有元素可以迭代
        while (it.hasNext()) {
            // 3. 循环内部获取元素
            String s = it.next();
            System.out.println(s);
        }
    }
}

cursor代表内部的指针,默认值为0

迭代器源码解析:

java 复制代码
//源码
public Iterator<E> iterator() {
        return new Itr();
    }
​
    /**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;
​
        // prevent creating a synthetic constructor
        Itr() {}
​
        public boolean hasNext() {
            return cursor != size;
        }
​
        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
复制代码

注意:在循环过程中next方法最好只调用一次

会抛出NoSuchElementException异常,表示没有元素,指针已经指空了,不能再往后指了

2、增强for循环

简化迭代器的代码书写 它是JDK5之后出现的,其内部原理就是一个Iterator迭代器

java 复制代码
格式:
for(元素的数据类型变量名:数组或者集合){}
​
//输入list.for
for (String s: list){
    System.out.println(s);
}
package com.itheima.collection;
​
import java.util.ArrayList;
import java.util.Collection;
​
public class CollectionDemo3 {
    /*
        集合通用遍历方式 - 增强 for 循环
     */
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        c.add("张三");
        c.add("李四");
        c.add("王五");
​
        //遍历集合时,底层(字节码文件)会转换成迭代器
        for (String s : c) {
            System.out.println(s);
        }
​
        int[] arr = {11, 22, 33, 44, 55};
​
        //遍历数组时,底层(字节码文件)是数组表示的
        for (int i : arr) {
            System.out.println(i);
            //System.out.println(arr[i]);
            //这里的i不是索引,所以不能用数组的索引遍历
        }
    }
}

3、forEach方法

是一个默认方法,所有的单列集合都可以直接调用该方法

java 复制代码
package com.itheima.collection;
​
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
​
public class CollectionDemo4 {
    /*
        集合通用遍历方式 - foreach 方法
     */
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        c.add("张三");
        c.add("李四");
        c.add("王五");
        //forEach方法中的Consumer参数是一个函数式接口,所以可以用Lambda表达式
        c.forEach(s -> System.out.println(s));
​
        Collection<Integer> c2 = new ArrayList<>();
        c2.add(111);
        c2.add(222);
        c2.add(333);
        c2.forEach(num -> System.out.println(num));
        //可以改为方法引用形式
        //c2.forEach(System.out::println);
    }
}

所有的单列集合都可以用以上三种方式遍历

四、方法引用

方法引用是JDK8开始出现,主要的作用,是对Lambda表达式进行进一步的简化 方法引用使用一对冒号:: 通过方法的名字来指向一个方法 可以使语言的构造更紧凑简洁,减少冗余代码

方法调用 -> MethodReference.change(s);

方法引用 -> MethodReference::change

java 复制代码
Collection<string> c = new ArrayList<>();
c.add("张三");
c.add("李四");
c.add("王五");
​
c.forEach(System.out::println);
"ABC".toLowerCase():将字母转化为小写字母

toUpperCase():转化为大写字母

//匿名内部类:方法调用的形式
list.forEach(new Consumer<String>() {
    @override
    public void accept(String s) {
        MethodReference.change(s);
    }
});
​
//变为Lambda表达式:方法调用的形式
list.forEach(s->MethodReference.change(s));
​
//再变为方法引用
list.forEach(MethodReference::change);
//参数去哪了?
//可推导即可省略:因为上面的匿名内部类正好传了一个参数s过去,所以可以推导参数为字符串s
  • 代码案例

    java 复制代码
    package com.itheima.reference;
    ​
    import java.util.ArrayList;
    ​
    public class MethodReference {
        /*
            方法引用是 JDK8 开始出现,主要的作用,是对 Lambda 表达式进行进一步的简化
    ​
            方法引用使用一对冒号 ::
            通过方法的名字来指向一个方法
            可以使语言的构造更紧凑简洁,减少冗余代码
    ​
            方法调用 -> MethodReference.change(s);
            方法引用 -> MethodReference::change
         */
        public static void main(String[] args) {
            ArrayList<String> list = new ArrayList<>();
            list.add("Hello");
            list.add("Hi");
            list.add("HEIma");
    ​
            MethodReference mr = new MethodReference();
    ​
            list.forEach(mr::change);
        }
    ​
        public void change(String s) {
            System.out.println(s.toLowerCase());
        }
    }

五、List接口

列表迭代器 (ListIterator):list接口下特有的迭代器

java 复制代码
package com.itheima.list;
​
import com.itheima.pojo.Student;
​
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
​
public class ListDemo1 {
    /*
        List接口特点: 存取有序, 有索引, 可以存储重复的
​
        遍历方式:
        1. 迭代器
        2. 增强for
        3. foreach方法
        4. 普通for循环
        5. 列表迭代器 (ListIterator)
     */
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        list.add(new Student("张三", 23));
        list.add(new Student("李四", 24));
        list.add(new Student("王五", 25));
        list.add(new Student("王五", 25));
​
        // 1. 迭代器
        Iterator<Student> it = list.iterator();
        while (it.hasNext()) {
            Student stu = it.next();
            System.out.println(stu);
        }
​
        System.out.println("------------------------------");
​
        // 2. 增强for
        for (Student stu : list) {
            System.out.println(stu);
        }
​
        System.out.println("------------------------------");
​
        // 3. foreach方法
        list.forEach(System.out::println);
​
        System.out.println("------------------------------");
​
        // 4. 普通for循环
        for (int i = 0; i < list.size(); i++) {
            Student stu = list.get(i);
            System.out.println(stu);
        }
​
        System.out.println("------------------------------");
​
        // 5. 列表迭代器 (ListIterator)
        //这两个方法了解即可
        ListIterator<Student> listIt = list.listIterator();
        System.out.println("列表迭代器正序遍历: ");
        while(listIt.hasNext()){
            Student stu = listIt.next();//正序遍历的方法
            System.out.println(stu);
        }
​
        System.out.println("列表迭代器倒序遍历: ");
        while(listIt.hasPrevious()){
            Student stu = listIt.previous();//倒序遍历的方法
            System.out.println(stu);
        }
    }
}
  • 常见异常:并发修改异常(ConcurrentModificationException)

    java 复制代码
    public class ConcurrentModificationException
    extends RuntimeException
    //当不允许这样的修改时,检测到对象的并发修改的方法可能抛出此异常。
        
    //使用迭代器遍历集合的过程中,调用了集合对象的添加,删除方法,就会出现此异常

    代码案例:

    java 复制代码
    package com.itheima.list;
    ​
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    import java.util.ListIterator;
    ​
    public class ListDemo2 {
        /*
            并发修改异常: ConcurrentModificationException
    ​
            使用迭代器遍历集合的过程中,调用了集合对象的添加,删除方法,就会出现此异常
    ​
            解决方案: 不允许使用集合的添加或删除方法, 就使用迭代器自身的添加或删除.
         */
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("张三");
            list.add("李四");
            list.add("王五");
            list.add("赵六");
            //获取列表迭代器,因为只有他有添加方法 list.listIterator.var直接生成
            ListIterator<String> it = list.listIterator();
            while (it.hasNext()) {
                String name = it.next();
                if ("李四".equals(name)) {
                    it.remove();
                    it.add("赵四");
                }
            }
    ​
            System.out.println(list);
        }
    }

六、数据结构(栈、队列、数组、链表)

栈和队列

数组:

优点:查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同 ​ 弊端:增、删效率低:新增或删除数据的时候,都有可能大批量的移动数组中其他的元素

链表:包括自己的地址、数据、下一个的地址

链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。

链表查询慢,无论查询哪个数据都要从头开始找。

链表增删相对快

栈:后进先出,先进后出。 队列:先进先出,后进后出。 数组:内存连续区域,查询快,增删慢。 链表:元素是游离的,查询慢,首尾操作极快。

七、ArrayList类

ArrayList底层是基于数组实现的,根据查询元素快,增删相对慢

ArrayList底层是数组结构的,数组默认长度为10 当数组添加满了之后,会自动扩容为1.5倍

java 复制代码
/*ArrayList 源码解析
使用空参构造器创建的集合,在底层创建一个**默认长度为0**的数组
添加**第一个元素时**,底层会创建一个新的长度为10的数组
存满时,会扩容1.5倍*/
//未调用add方法之前的源码
public ArrayList(){
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
transient Object[] elementData;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

八、LinkedList类

LinkedList底层基于双链表实现的,查询元素慢,增删首尾元素是非常快的

九、泛型

泛型介绍 JDK5引入的,可以在编译阶段约束操作的数据类型,并进行检查 泛型的好处: 统一敬据类型 将运行期的错误提升到了编译期 注意事项 泛型中只能编写引用数据类型

泛型用E来表示

使用泛型:

java 复制代码
package com.itheima.generics;
​
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
​
public class GenericsDemo1 {
    /*
        泛型介绍 : JDK5引入的, 可以在编译阶段约束操作的数据类型, 并进行检查
​
        泛型如果没有指定具体的类型, 默认为Object
​
        泛型的好处 :
                    1. 统一数据类型
                    2. 将运行期的错误提升到了编译期
     */
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
        list.add("abc");
        list.add("abc");
​
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String s = it.next();//it.next
            System.out.println(s.length());
        }
    }
}
复制代码

使用E不指定泛型类型

泛型常见的标识符:

E : Element 元素

T : Type 类型

K : Key 键

V : Value 值

java 复制代码
package com.itheima.generics;
​
public class GenericsDemo2 {
    /*
        泛型常见的标识符:
​
            E : Element 元素
            T : Type 类型
            K : Key 键
            V : Value 值
     */
    public static void main(String[] args) {
        Student<Integer> stu1 = new Student<>();
        stu1.setE(10);
    }
}
​
class Student<E> {//在创建对象的时候由调用者自己指定数据类型
    private E e;
​
    public E getE() {
        return e;
    }
​
    public void setE(E e) {
        this.e = e;
    }
}

泛型类

java 复制代码
//在创建对象的时候由调用者自己指定数据类型
public class ArrayList<E>{
    public boolean add(E e){
    }
}
​
public class A{
    public static void main(string[] args) {
        ArrayList<String> list =new ArrayList<>();
list.add(String e);
    }
}
​
public class A{
    public static void main(string[] args) {
        ArrayList<Integer> list =new ArrayList<>();
    list.add(Integer e);
    }
}

泛型方法

在方法当中加泛型

java 复制代码
package com.itheima.generics;
​
public class GenericsDemo3 {
    /*
        泛型方法
​
              非静态方法: 跟着类的泛型去匹配的
                            类的泛型, 是在创建对象的时候确定到具体数据类型.
​
              静态方法: 调用方法的时候, 传入实际参数, 在这时候确定到具体的数据类型
                            注意: 静态方法如果声明了泛型, 必须声明出自己独立的泛型.
     */
    public static void main(String[] args) {
​
        String[] arr1 = {"张三", "李四", "王五"};
        Integer[] arr2 = {11, 22, 33};
        Double[] arr3 = {11.1, 22.2, 33.3};
​
        printArray(arr1);
        printArray(arr2);
        printArray(arr3);
​
    }
​
    //静态方法的泛型在创建的时候自己作为Tybe类型,需要声明处自己独立的泛型
    //调用方法的时候, 传入实际参数的时候确定具体类型的
    public static <T> void printArray(T[] arr) {
        System.out.print("[");
        for (int i = 0; i < arr.length - 1; i++) {
            System.out.print(arr[i] + ", ");
        }
        System.out.println(arr[arr.length - 1] + "]");
    }
}

泛型接口:

类实现接口的时候,如果接口带有泛型,有两种操作方式 1.类实现接口的时候,直接确定类型 2.保持接口的泛型,等创建对象的时候再确定

java 复制代码
package com.itheima.generics;
​
​
import java.util.ArrayList;
import java.util.List;
​
public class GenericsDemo4 {
    /*
        泛型接口
                1. 实现类, 实现接口的时候确定到具体的类型
                2. 实现类实现接口, 没有指定具体类型, 就让接口的泛型, 跟着类的泛型去匹配
     */
    public static void main(String[] args) {
        InterCImpl<String> c1 = new InterCImpl<>();
        c1.show("abc");
​
        InterCImpl<Integer> c2 = new InterCImpl<>();
        c2.show(123);
    }
}
​
interface Inter<E> {//用户自己指定
    void show(E e);
}
​
//实现接口的时候确定到具体类型
class InterAImpl implements Inter<String> {
​
    @Override
    public void show(String s) {
​
    }
}
​
class InterBImpl implements Inter<Integer> {
​
    @Override
    public void show(Integer integer) {
​
    }
}
​
//实现类在实现接口的时候可以在创建对象的时候给类指定泛型
class InterCImpl<E> implements Inter<E> {
​
    @Override
    public void show(E e) {
​
    }
}

泛型通配符

可以接收任意类型

? : 任意类型

? extends E : 可以传入的是E, 或者是E的子类

? super E : 可以传入的是E, 或者是E的父类

java 复制代码
package com.itheima.generics;
​
import java.util.ArrayList;
​
public class GenericsDemo5 {
    /*
        泛型通配符
​
                ? : 任意类型
​
                ? extends E : 可以传入的是E, 或者是E的子类
​
                ? super E : 可以传入的是E, 或者是E的父类
     */
    public static void main(String[] args) {
​
        ArrayList<Coder> list1 = new ArrayList<>();
        list1.add(new Coder());
​
        ArrayList<Manager> list2 = new ArrayList<>();
        list2.add(new Manager());
​
        ArrayList<String> list3 = new ArrayList<>();
        list3.add("abc");
​
        method(list1);
        method(list2);
​
    }
​
    public static void method(ArrayList<? extends Employee> list) {
        for (Employee e : list) {
            e.work();
        }
    }
​
}
​
abstract class Employee {
    private String name;
    private double salary;
​
    public Employee() {
    }
​
    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
​
    public abstract void work();
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public double getSalary() {
        return salary;
    }
​
    public void setSalary(double salary) {
        this.salary = salary;
    }
​
    public String toString() {
        return "Employee{name = " + name + ", salary = " + salary + "}";
    }
}
​
class Coder extends Employee {
    @Override
    public void work() {
        System.out.println("程序员写代码...");
    }
}
​
class Manager extends Employee {
    @Override
    public void work() {
        System.out.println("项目经理分配任务...");
    }
}

总结:

泛型类 创建对象的时候确定具体类型 泛型方法 非静态:泛型是根据类的泛型去匹配的 静态:需要声明出自己独立的泛型 泛型接口: 类实现接口的时候,直接确定类型 保持接口的泛型,等创建对象的时候再确定 泛型限定: ?(任意类型) ?extendsE(只能接收E或者是E的子类) ?superE(只能接收E或者是E的父类)

相关推荐
ShiXZ2131 小时前
PDF-OCR文件识别篇(八):配置、运维与排错
java·运维·ocr·dubbo·springboot
彦为君2 小时前
Redis最新版本特性
java·数据库·redis·算法·bootstrap
码来的小朋友2 小时前
手把手教你用 Python + PyQt5 做一个可视化图片切图工具
开发语言·python·microsoft
格子软件2 小时前
2026年GEO优化系统源码解构:核心状态机与高并发流控深度剖析
java·vue.js·spring boot·vue·geo
weixin199701080162 小时前
[特殊字符]《京东订单API(jd.order.detail.get)对接ERP:企业认证+OAuth授权避坑指南》(附Python源码)
java·数据库·python
pW3g3lLuu2 小时前
在 VS Code 里直接改 JAR,我复刻了JarEditor
java·pycharm·jar
云烟成雨TD3 小时前
LangFlow 1.x 系列【3】入门案例
人工智能·python·agent
创世宇图3 小时前
【Python工程化实战】Python 服务的结构化日志体系:structlog + JSON 输出 + 日志分级策略
python·elk·structlog·结构化日志·可观测性
Tim_103 小时前
【C++】009、extern关键字
java·开发语言