在Java编程的浩瀚宇宙中,Java集合框架(Java Collections Framework)无疑是一颗璀璨的明珠,是每位Java开发者必须精通的核心知识点。它不仅提供了一套高效、灵活的数据结构,还极大地简化了数据的操作和管理。本文旨在通过深度剖析Java集合框架的精髓,帮助读者达到精通的水平,从而在CSDN等平台上获得95分以上的高度评价。
Java集合框架是一套设计用来表示和操作对象集合的类和接口的集合。它主要包括Collection
、List
、Set
、Queue
和Map
等核心接口,以及它们的众多实现类。
ArrayList
(基于数组实现,支持快速随机访问)和LinkedList
(基于链表实现,支持快速的插入和删除操作)。HashSet
(基于哈希表实现,查询效率极高)和TreeSet
(基于红黑树实现,元素按自然顺序或自定义比较器排序)。LinkedList
(作为队列使用时)和PriorityQueue
(优先级队列)。HashMap
(基于哈希表实现,支持快速查找)、TreeMap
(基于红黑树实现,键按自然顺序或自定义比较器排序)和LinkedHashMap
(维护插入顺序的哈希映射)。hasNext()
和next()
方法,用于检查是否还有下一个元素以及获取下一个元素。Collections.synchronizedCollection
方法将非线程安全的集合包装成线程安全的集合。但是,这种方法可能会导致性能下降,并且在迭代过程中修改集合时仍然可能抛出ConcurrentModificationException
异常。ConcurrentHashMap
、CopyOnWriteArrayList
等。这些类通过内部机制保证了线程安全,并且提供了更好的性能。ArrayList
;如果需要频繁进行插入和删除操作且不关心元素顺序,可以选择LinkedList
;如果需要快速查找元素且不关心元素顺序,可以选择HashSet
;如果需要元素有序且不允许重复元素,可以选择TreeSet
。ArrayList
时,可以通过指定初始容量来减少扩容操作带来的性能开销;在使用HashMap
时,可以通过指定合适的负载因子和初始容量来优化性能。尽量使用泛型:泛型的使用可以避免类型转换错误,提高代码的安全性和可读性。在定义集合时,应该尽量使用泛型来指定集合中元素的类型。
注意集合的边界条件:在添加元素到集合时,要注意集合的容量限制和边界条件。例如,在使用ArrayList
时,要注意其最大容量限制;在使用HashSet
时,要注意其不允许重复元素的特性。
合理使用迭代器:在遍历集合时,应该使用迭代器或增强型for循环来避免在遍历过程中修改集合导致的并发修改异常。如果需要在遍历过程中修改集合,可以使用迭代器提供的remove()
方法来安全地删除元素。
了解集合的内部实现:了解集合的内部实现原理有助于更好地使用和优化集合。例如,了解ArrayList
是基于数组实现的,可以理解其随机访问性能的优势和插入删除操作的劣势;了解HashMap
是基于哈希表实现的,可以理解其快速查找的性能优势和元素无序的特性。
1、Collection:基本接口
1.1 List 接口
特性:有序集合,可包含重复元素。
- ArrayList
特点:随机访问速度快,中间插入删除速度较慢尾部操作较快。
底层:使用动态数组实现,由于数组的存储结构是连续的内存块,通过索引可以直接定位到任何一个元素,时间复杂度为 O(1),因此随机访问速度非常快,但是进行插入或删除操作时,尤其是在中间位置,可能需要移动大量元素以保持数组的连续性,因此在中间位置插入和删除操作较慢,在数组头尾的话插入和删除操作则较快
- LinkedList
特点:插入删除速度快,随机访问速度较慢。
底层:使用双向链表实现,每个节点包含三个部分,元素值、指向前一个节点的引用、指向下一个节点的引用,某个位置插入一个新元素,只需要调整相关节点的 next 和 previous 引用即可,删除一个元素时,只需要将该节点的前一个节点的 next 指向该节点的下一个节点,同时将下一个节点的 previous 指向前一个节点即可,时间复杂度通常为 O(1),因此插入删除速度快,但是随机访问某个位置的元素需要从链表头部或尾部开始,逐个节点进行遍历,直到找到目标位置,即O(n) 的时间复杂度
- Vector
特点:类似 ArrayList,但线程安全。
底层:Vector 对每个方法(add(), remove(), get()等)都进行了同步处理(synchronized 关键字),以确保线程安全
- Stack:
特点:继承自 Vector,实现栈的功能,支持后进先出(LIFO)。
底层:LIFO 意味着最后添加的元素最先被移除,比如通过push(E item)将元素按顺序 1、2、3 压入栈,则通过pop()移除并返回栈顶元素的顺序是3、2、1
1.2 Set 接口
特性:无序集合,不允许重复元素。
- HashSet:
特点:实现了Set,基于哈希表实现,不保证元素顺序。
底层:实际上是一个 HashMap 对象。具体来说,HashSet 使用 HashMap 的键 (key) 来存储集合中的元素,而所有的值 (value) 都使用一个固定的对象 PRESENT 来代替,HashSet 确保了元素的唯一性,因为 HashMap 不允许键重复。
- LinkedHashSet:
特点:继承自HashSet,具有插入顺序的哈希表实现。
底层:是一个 LinkedHashMap
- TreeSet:
特点:实现了Set,不允许重复元素,元素是有序的,插入、删除、搜索操作效率较高。
底层:TreeSet 通过 TreeMap 实现,调用 TreeMap 的 put、remove、clear等方法完成各种操作
1.3 Queue 接口
特性:用于按顺序排队的元素。
- PriorityQueue:
特点:实现了Queue ,元素按优先级排序。
底层:基于优先级堆(二叉堆)实现,默认是最小堆实现,在插入和删除时通过上浮和下沉操作来维护堆的性质,从而排序元素的优先级,元素按优先级顺序出队,优先级高的元素会比优先级低的元素先出队
- Deque:继承自Queue ,双端队列,可从两端进行插入和删除。
- ArrayDeque:
特点:实现了Deque,高效的两端操作和随机访问操作,对内存连续性要求高。
底层:使用循环数组(circular array)实现,将数组首尾相连,使用两个指针(head 指针指向队列的第一个元素,tail 指针指向队列的下一个插入位置)来管理循环数组,不直接支持在中间进行插入和删除操作。在两端插入和删除元素的时间复杂度为 O(1),但在扩展数组时可能需要 O(n) 的时间,通过索引访问元素的时间复杂度为 O(1)
- LinkedList:
特点:实现了Deque,既高效的两端操作和动态内存扩展收缩。
底层:使用双向链表(Doubly Linked List)实现,每个节点包含数据以及指向前一个和后一个节点的指针,由于每个节点需要额外的指针空间,内存开销较大。在链表的两端插入和删除元素的时间复杂度为 O(1),访问任意位置的元素需要从头或尾遍历,时间复杂度为 O(n)
2、Map:存储键值对的接口
- HashMap:
特点:不保证顺序的键值对映射。
底层:HashMap 是基于哈希表(哈希桶)实现的,通过对象的 hashCode 方法计算出一个哈希码,再经过某种散列函数转换为哈希表中的索引,然后在哈希表检查该索引位置是否已经有元素,没用则将元素存储在哈希表的相应位置(桶),有则处理哈希冲突(通常通过链地址法(意味着同一个桶(Bucket)中可能会存储多个数据),即链表(Java 8 及之后当链表长度超过一定阈值(默认是8)时,会将链表转换成红黑树确保操作性能),或者开放地址法(所有元素存储在哈希表的数组中,不使用额外的数据结构,而通过某种探测策略在数组中寻找下一个可用位置来存放冲突的元素,比如线性探测,就是按照一定的步长(计算下一个存储位置的增量值,比如1)依次检查下一个位置直到找到一个空位)。另外经过使用哈希表生成索引来存储元素,而不是按照插入顺序,所以无法保证键值对的顺序,不过这也使得插入、删除和查找操作通常可以在常数时间内完成,即 O(1) 时间复杂度
- LinkedHashMap:
特点:具有插入顺序的键值对映射。
底层:在HashMap的基础上,还维护了一个双向链表来记录元素的插入顺序。每个节点不仅包含键、值和哈希码,还包含指向前后节点的指针
- TreeMap:
特点:有序的键值对映射,键默认按自然顺序排序。
底层:TreeMap 使用红黑树来实现的,它是一种自平衡的二叉搜索树,操作的时间复杂度为O(log n) 。节点存储着父子节点的引用和键值对数据。由于红黑树的结构依赖于对节点进行比较,以决定节点在树中的位置,来保持红黑树的有序性和自平衡,所以可以保证存储有序的键值对映射。此默认比较器为按照自然顺序的比较器,如果需要按不同的顺序存储数据,就需要使用不同的比较器在创建 TreeMap 时提供相应的比较器
- Hashtable:
特点:线程安全的哈希表实现。
底层:通过哈希表来实现存储和快速查找,所有方法都是同步的。Hashtable 不允许键或值为 null
- Properties:
特点:专门用于维护配置文件中的键值对。
底层:继承自 Hashtable
eg:读取配置文件
eg:写入配置文件
eg:获取属性值
二、特点
- 类型安全
-通过泛型保证类型安全
泛型(Generics)是 Java 中的一种机制,它允许在定义类、接口和方法时使用类型参数。。这些类型参数在实际使用时可以指定具体的类型,从而确保在编译时进行类型检查,保证类型安全。(比如List接口定义为List
- 动态扩展
-大多数集合类可以根据需要动态扩展
指的是集合类在需要时可以自动调整其容量,以适应添加新元素的需求。这一特性主要应用于那些基于数组实现的集合类,如 ArrayList (默认情况下,ArrayList 的初始容量是 10,当数组容量不足时,常会将其容量增加约 50%。例如,如果当前容量是 10,新的容量将是 15)和 Vector(默认的扩展机制是将容量翻倍)
- 高性能
-通过优化算法和数据结构,提供高效的操作
不同的集合类采用了不同的数据结构,以提高其在特定操作上的性能(比如ArrayList动态数组,LinkedList双向链表,HashSet/HashMap哈希表,TreeSet/TreeMap红黑树);使用了多种高效的算法来优化操作性能(比如Collections.binarySearch二分查找,HashMap哈希算法,Collections.sort归并排序算法);并发优化(比如Collections.synchronizedList 和 Collections.synchronizedMap 来动态地创建线程安全的集合,比Vector 和 Hashtable性能更好)
- 丰富的功能
-提供丰富的操作方法,如排序、搜索、线程安全等
主要体现在它提供了许多内置的方法和工具类,如下
三、工具类
Collections
提供操作集合的静态方法,如排序、搜索、同步集合创建等。
它提供了一系列静态方法,用于操作和处理集合
对自身的操作
增
1、填充
fill(List super T> list, T obj):用指定元素填充列表。
查
2、查找
binarySearch(List extends Comparable super T>> list, T key):使用二分法搜索指定列表,以获得指定对象的索引(适用于有序集合,效率较高)
改
3. 排序
sort(List
sort(List
4、反转
reverse(List> list):反转指定列表中元素的顺序
5、交换
swap(List> list, int i, int j):交换列表中指定位置的元素
集合间的操作
查
1、集合最值
min(Collection extends T> coll):返回给定集合中的最小元素
max(Collection extends T> coll):返回给定集合中的最大元素
2、频率统计
frequency(Collection> c, Object o):返回指定集合中等于指定对象的元素个数
改
3、拷贝
copy(List super T> dest, List extends T> src):将源列表中的所有元素复制到目标列表中(目标集合 dest 必须至少与源集合 src 一样长,否则会抛出 IndexOutOfBoundsException 异常)
执行上述代码后,dest 集合的内容将会是:
[a, b, c]
4、同步
synchronizedList(List
对比Vector由于只在需要时才同步,并且可以选择性同步,性能通常比 Vector 更好
5、不可变
unmodifiableList(List extends T> list):返回指定列表的不可修改视图
6、合并
addAll(Collection super T> c, T... elements):将所有指定元素添加到指定集合中
Java集合框架是Java编程中的一个重要组成部分,它提供了一套高效、灵活的数据结构和操作方法。通过对集合框架的深入学习和实践,我们可以更加高效地管理数据、优化程序性能、提升代码质量。未来,随着Java技术的不断发展和演进,集合框架也将不断完善和丰富其功能和应用场景。希望本文能够帮助读者在Java集合框架的学习之路上取得更大的进步和成就!
超全Java集合框架讲解(建议收藏)-CSDN博客