Java集合与泛型

目录

1.ArrayList 底层原理 & 源码解析
2.LinkedList 底层原理 & 源码解析
3.迭代器底层原理与并发修改异常
4.泛型全套知识点(基础、伪泛型、泛型类 / 方法 / 接口、通配符)

一、ArrayList 底层原理与源码详解

1.基础核心结论

  1. 底层:Object 数组 elementData存储元素
  2. 空参构造初始化:创建长度为 0 的空数组,首次 add 才扩容到默认容量 10
  3. size 变量两层含义:当前集合元素个数;下一个待存入元素索引
  4. 扩容规则
    ○单个 add 添加:扩容至原长度 1.5 倍(oldCapacity >> 1)
    ○addAll 批量添加:若 1.5 倍容量仍不够,直接扩容至「原有元素数量 + 新增元素总数」
  5. 扩容底层依靠Arrays.copyOf():创建新数组,复制全部旧元素

2. 空参构造源码流程

  1. new ArrayList ():elementData 赋值为长度 0 的空数组,size=0
  2. 第一次调用 add ()
    ○判断 size 与数组长度相等,执行 grow () 扩容
    ○旧容量 oldCap=0,进入 else 分支,取默认容量 10 与最小需求容量 1 的最大值
    ○创建长度为 10 的新数组,赋值给 elementData
    ○元素存入 size 索引,size++

3. 普通扩容流程(数组存满,添加单个元素)

  1. 数组已满 (size == 数组长度),触发 grow
  2. oldCap = 10,默认扩容增量 = 10>>1=5,预期新容量 15
  3. minCapacity = size+1=10+1=1
  4. 取预期容量 15 作为新长度,Arrays.copyOf 复制所有元素
  5. 新数组存入新增元素,size 自增

4. 批量 addAll 扩容特殊逻辑

一次性添加大量元素时,计算最少需要增加的容量(新增元素个数),和默认扩容增量对比,取更大值作为扩容增量,保证能放下所有数据。

5. ArrayList 核心变量

  1. transient Object\[\] elementData:底层存储数组
  2. int size:实际元素个数
  3. modCount:集合修改计数器(add/remove 都会自增,用于迭代器校验并发修改)

6. 补充 API

addAll (集合):批量将传入集合所有元素添加至当前 ArrayList

二、LinkedList 底层原理与源码详解

1.基础核心结论

  1. 底层:双向链表,无数组,不存在扩容机制
  2. 内部私有内部类 Node:链表节点
  3. 维护三个全局变量:size(节点总数)、first(头节点)、last(尾节点)
  4. 优势:头尾增删速度极快;中间位置增删需要遍历查找节点,效率低
  5. 劣势:随机查询效率低,必须遍历链表

2. Node 节点结构(三成员)

java 复制代码
private static class Node<E> {
    E item;        // 当前存储元素
    Node<E> prev;  // 上一节点地址
    Node<E> next;  // 下一节点地址
}

3. 空参构造

new LinkedList ():size=0,first=null,last=null,无任何节点

4. add () 尾部添加流程(底层 linkLast ())

  1. 用局部变量 l 记录原尾节点 last
  2. 新建 Node:prev=l,item = 待添加元素,next=null
  3. 将新节点赋值给 last
  4. 判断原尾节点 l 是否为 null(集合为空)
    ○为空:first 也指向新节点(链表只有一个节点,头尾相同)
    ○不为空:原尾节点 l.next = 新节点,完成双向绑定
  5. size++,modCount++

5. LinkedList 独有 API(操作首尾)

addFirst () 头部添加 | addLast () 尾部添加(等价 add)

getFirst () 获取头元素 | getLast () 获取尾元素

removeFirst () 删除头 | removeLast () 删除尾

三、迭代器底层原理 & 并发修改异常

1.迭代器获取方式

list.iterator ():返回内部类 ITR 迭代器对象

2. ITR 迭代器核心成员变量

1.cursor:迭代器指针,默认初始值 0(指向待读取索引)

2.lastRet:上一次读取元素的索引,初始 - 1

3.expectedModCount:创建迭代器时记录的集合修改次数

3. 核心方法底层逻辑

hasNext()

判断cursor != size,不等代表还有未读取元素,返回 true;反之无元素

next () 执行流程

  1. 先执行 checkForModification () 校验并发修改
  2. 记录当前 cursor 到局部变量 i
  3. cursor 指针 + 1(后移)
  4. lastRet = i,返回底层数组 i 对应元素

NoSuchElementException 报错场景

cursor 等于 size 后继续调用 next,无元素可读取,抛出异常

4. 并发修改异常 ConcurrentModificationException

核心变量 modCount

集合每 add/remove 一次,modCount 自增,记录集合结构修改总次数。

触发原因

  1. 创建迭代器时,迭代器复制当前 modCount 存入 expectedModCount
  2. 遍历过程中,使用集合 add/remove 方法修改集合
  3. 调用 next () 时执行校验:if (modCount != expectedModCount) 直接抛异常

规避方案

遍历过程中,不要用集合对象增删元素;如需删除使用迭代器自身 remove () 方法。

四、泛型全套知识点

4.1 泛型基础概念

1.诞生版本:JDK5 引入

2.作用:编译阶段约束集合存储数据类型,统一元素类型

3.格式:类 / 接口 / 方法后加<类型>,仅支持引用类型,不能使用基本类型(int→Integer)

无泛型的弊端

1.集合默认存 Object,任意类型都能存入

2.取出元素需要强制类型转换,极易抛出 ClassCast 类型转换异常

3.类型错误运行期才暴露,难以排查

泛型带来两大好处

1.编译期校验类型,不符合直接报错,提前规避问题

2.取出元素无需强转,简化代码

4.2 Java 泛型本质:伪泛型(泛型擦除)

1.泛型仅存在于.java 源码编译阶段

2.编译为.class 字节码时,所有泛型标识全部擦除(泛型擦除)

3.底层实际存储仍为 Object,取出时编译器自动插入强制转换代码

4.设计目的:兼容 JDK5 之前无泛型的旧代码,无需大规模改造历史项目

4.3 泛型三大载体

1.泛型类

定义:类名后<E/T/K/V>,类内全局可使用该不确定类型

java 复制代码
public class MyArrayList<E> {
    Object[] arr = new Object[10];
    public boolean add(E e){
        arr[size++] = e;
        return true;
    }
    public E get(int index){
        return (E) arr[index];
    }
}

使用:创建对象时确定泛型MyArrayList list = new MyArrayList<>();

2.泛型方法

修饰符后定义,仅当前方法可用该不确定类型,不受类泛型限制

适用场景:仅单个方法类型不确定,无需整个类泛型

java

java 复制代码
// 工具类静态泛型方法示例
public static <E> void addAll(List<E> list, E... elements){
    for(E e : elements) list.add(e);
}

可变参数E...:底层数组,可传入任意多个同类型参数

3.泛型接口

两种实现方式

1.实现类直接指定具体类型

2.实现类延续泛型,创建对象时再确定

4.4 泛型继承与通配符

关键规则:泛型不具备继承性,存储的数据具备继承性

示例:ArrayList 不能接收 ArrayList 对象

但ArrayList中可以 add (new Dog ())、add (new Cat ())

通配符?(代表任意未知类型)

1.? extends E:上界限定,只能是 E 或 E 的子类;只读,不能存数据

2.? super E:下界限定,只能是 E 或 E 的父类;可以存入 E 及其子类

3.单独?:无限制,任意类型

使用场景区分

1.类型全程固定:泛型类 / 泛型方法

2.类型不确定,但限定在某条继承链:通配符 extends/super

4.5 泛型细节注意点

1.泛型尖括号内只能写包装类,禁止 byte/short/int 等基本类型

2.指定泛型后,可存入当前类型、其子类对象

3.不写泛型则默认原始类型,底层 Object,失去类型校验

4.通配符和泛型方法区分:泛型方法可读写,单纯?通配符有读写限制

五、本章对比总结

泛型核心速记

1.编译检查,字节码擦除,伪泛型

2.三类:泛型类、泛型方法、泛型接口

3.泛型无继承,元素有继承

4.? extends 存子类,? super 可以存父类

相关推荐
liulilittle1 小时前
[OPENPPP2] ssea 算法详解(中文)
算法·x86·x64·simd·openppp2·ssea·base94
不才不才不不才1 小时前
Spring 源码系列(14): JDK 动态代理 vs CGLIB,以及拦截器责任链
java·开发语言·spring
mifengxing1 小时前
计算机组成原理——存储器系统
开发语言·考研·计算机组成原理·复习笔记·计算机408
碧海银沙音频科技研究院1 小时前
蓝牙耳机使用WSDF5361锂保芯片进入船运模式实现方法
嵌入式硬件·算法
过期的秋刀鱼!2 小时前
学习曲线-过拟合和欠拟合要做什么以及原因
人工智能·python·深度学习·算法·机器学习·模型评估
流云鹤2 小时前
05Java学习day(5)
java·学习
用户3126874877202 小时前
Spring 事件机制到底怎么传播的?ApplicationEvent 源码拆解
java·spring
m沐沐2 小时前
【自然语言处理】词向量转换与中文文本情感分类——从CountVectorizer到朴素贝叶斯
人工智能·算法·机器学习·自然语言处理·分类·中文分词·词向量转换
默辨2 小时前
Spring AI Alibaba 核心知识点
java·ai·spring ai·spring alibaba