plaintext
Java集合框架
├── Collection (接口):单列
│ ├── List (接口):有序、可重复、有索引
│ │ ├── ArrayList (实现类)
│ │ ├── LinkedList (实现类)
│ │ └── Vector (实现类,线程安全,已基本淘汰)
│ ├── Set (接口):无序、不可重复、无索引
│ │ ├── HashSet (实现类)
│ │ │ └── LinkedHashSet (子类,有序)
│ │ └── TreeSet (实现类,可排序,默认升序)
│ └── Queue (接口):队列(先进先出FIFO)
│ └── LinkedList (同时实现Queue,常用)
└── Map (接口)::双列,键值对,键唯一,值可重复
├── HashMap (实现类)
│ └── LinkedHashMap (子类,有序)
├── TreeMap (实现类,可排序)
└── Hashtable (实现类,线程安全,已基本淘汰)
Collection
遍历:1. iterator 2. 增强for循环 3. Lambda
关键:1. 增强for循环在都会编译成iterator迭代器;2. Lambda:forEach是语法糖底层还是调用iterator迭代器;3.集合存储的都是对象,基本类型会自动装箱;4. iterator遍历中不能删除新增
拓展:1. 解决 iterator遍历删除问题,匹配删除时手动i--,iterator的remove封装了;2. Iterator 遍历的核心是 "快速失败" 原则:遍历过程中,若集合的结构(元素个数)被修改(新增 / 删除),且修改操作不是通过迭代器自身的方法完成,迭代器会立即检测到并抛出异常,避免遍历出现不可预期的结果,底层逻辑是集合内部维护一个modCount(修改次数)变量,迭代器创建时会记录这个值(expectedModCount)。遍历中每次调用next()都会校验modCount == expectedModCount,不相等则抛异常;3. ListIterator是 Iterator 的子接口(仅 List 集合支持),它的add()方法会同步更新expectedModCount;4. 增强for循环和Lambda在操作遍历删除都只是集合的remove,而不是iterator封装的remove,可能造成错误。
List
ArrayList
实现原理:1. 存储:数组(Object[])- 连续存储;2. 扩容:存储一个对象,对象能有数组elementData和size代表当前存储下标和数组已填充大小,当填入一个数据时数组elementData为10初始大小,当存满时,会扩容1.5倍,如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准;
遍历:1. for循环
关键:1. ArrayList转换为数组 toArray()返回Object[];2. ArrayList转换为指定类型数组 toArray(new String[]) ;3. addAll () 执行的是浅拷贝(浅复制),而非深拷贝。
规范:1. List list = new ArrayList<>(); 多态写法
应用场景:1. ArrayList适合:根据索引l查询数据比如根据随机索引取数据(高效)!或者数据量不是很大时;2. ArrayList不适合: 数据量大的同时又要频繁的进行增删操作!
LinkedList
实现原理:双链表,
特点:查询慢(比较单链表快,可以通过判定索引靠前还是靠后判定从头遍历还是从尾遍历),增删相对较快,但对首尾元素进行增删改查的速度是极快的。
应用场景:1. 栈、堆;
Set
注意:Set要用到的常用方法,基本上就是collection提供的!!自己几乎没有额外新增一些常用功能!
HashSet
实现原理:存储:基于哈希表实现。哈希表是一种增删改查数据,性能都较好的数据结构。操作:1. 创建一个默认长度16的数组,默认加载因子为0.75,数组名table
- 使用元素的哈希值对数组的长度求余计算出应存入的位置
- 判断当前位置是否为null,如果是null直接存入(去重,数组和链表或红黑树都会判断)
- 如果不为null,表示有元素,则调用eguals方法比较
相等,则不存;不相等,则存入数组
- JDK 8之前,新元素存入数组,占老元素位置,老元素挂下面
- JDK8开始之后,当链表长度超过8,且数组长度>=64时,新元素直接挂在老元素下面
去重:哈希函数计算位置后通过hasCode判断在数组中是否重复,不重复,在逐一equal比较链表或红黑树节点,通过对象的重写hasCode和equal
拓展:树:二叉查找树(二叉排序树)、平衡二叉树、红黑树(自平衡的二叉树);关系:哈希表 :是一种通用的数据结构(数组 + 链表 / 红黑树),目标是通过哈希函数实现 O (1) 级别的增删查;HashMap :Java 中哈希表的具体实现类,底层是 "数组(桶)+ 链表 + 红黑树"(JDK 1.8+);HashSet:是 HashMap 的 "包装类",通过复用 HashMap 的键(Key)实现去重,值(Value)是一个固定的空对象。
LinkedHashSet
实现原理:排序:每个元素都额外的多了一个双链表的机制记录它前后元素的位置,当插入第一个元素确定位于数组的位置,此时标记首节点变量和标记尾节点变量都指向第一个元素,当插入第二个元素时,将标记尾节点变量地址和新插入地址建立双向,然后将标记尾节点变量更新地址
TreeSet
实现原理:基于红黑树实现
排序规则:默认升序排序,按照元素的大小,由小到大排序,自定义排序规则:方式一:让自定义的类(如学生类)实现comparable接口,重写里面的compareTo方法来指定比较规则。方式二:通过调用TreeSet集合有参数构造器,可以设置comparator对象(比较器对象,用于指定比较规则。public TreeSet(Comparator<? super E> comparator)如果两种排序都存在,TreeSet就近选择自已自带的比较器对象进行排序(即方法二);去重:根据排序规则比较的值去重
拓展:红黑树 :一种自平衡的二叉查找树(BST),保证任意节点的左右子树高度差不超过两倍,查询 / 增删时间复杂度稳定在 O(logn);TreeMap :Java 中红黑树的具体实现类,存储键值对,且键会自动排序(自然排序 / 自定义排序);TreeSet:TreeMap 的 "包装类",将元素作为 TreeMap 的 Key,Value 是固定空对象,复用TreeMap 的排序 + 去重逻辑。
应用场景汇总
1、如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据?
用ArrayList集合(有序、可重复、有索引l),底层基于数组的。(常用)
2、如果希望记住元素的添加顺序,且增删首尾数据的情况较多?
用LinkedList集合(有序、可重复、有索引l),底层基于双链表实现的。
3。如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快?
用HashSet集合(无序,不重复,无索引),底层基于哈希表实现的。(常用)
4.如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快?
用LinkedHashSet集合(有序,不重复,无索引l),底层基于哈希表和双链表。
5.如果要对元素进行排序,也没有重复元素需要存储?且希望增删改查都快?
用TreeSet集合,基于红黑树实现。
iterator迭代器
原理:迭代器的本质是为每个集合实例创建一个 "专属遍历对象" ,这个对象持有当前遍历的位置(状态),并封装了 "如何遍历当前集合" 的逻辑(不同集合的遍历逻辑不同,但对外接口统一)。
以ArrayList的迭代器(内部类Itr)为例,看其核心原理:
java
public class ArrayList<E> {
private Object[] elementData; // 底层数组
private int size; // 元素个数
private int modCount; // 集合修改次数(快速失败用)
// ArrayList的内部迭代器实现
private class Itr implements Iterator<E> {
int cursor; // 【遍历状态】下一个要返回的元素的索引(初始为0)
int lastRet = -1; // 【遍历状态】上一个返回的元素的索引(初始为-1)
int expectedModCount = modCount; // 记录迭代器创建时的集合修改次数
// 1. 判断是否有下一个元素:游标没到size则有
@Override
public boolean hasNext() {
return cursor != size;
}
// 2. 获取下一个元素:移动游标,返回元素
@Override
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]; // 记录上一个元素的索引
}
// 3. 安全删除:删除上一个返回的元素
@Override
public void remove() {
if (lastRet < 0) throw new IllegalStateException();
checkForComodification(); // 快速失败校验
try {
ArrayList.this.remove(lastRet); // 调用集合的删除方法
cursor = lastRet; // 游标回退(避免跳过元素)
lastRet = -1; // 重置lastRet
expectedModCount = modCount; // 同步修改次数
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
// 快速失败校验:集合修改次数≠迭代器记录的次数则抛异常
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
Collections工具类
- public static boolean addAll(collection<? super T> c, T...elements)为集合批量添加0个或多个元素
- public static void shuffle(List<?> list): 打乱List集合中的元素
- public static void sort(List list):对List集合中的元素进行升序排序。
- public static void sort(List list, Comparator<? super T> c)
对List集合中元素,按照比较器对象指定的规则进行排序,方式一:让自定义的类(如学生类)实现comparable接口,重写里面的compareTo方法来指定比较规则。方式二:通过有参数方法,可以传入comparator对象