常见的集合框架
JAVA的集合框架可以分成两类。
-
Collection,主要有List、vector、set、queue
-
List代表有序,可重复的集合,像动态数组ArrayList和链表LinkedList
-
Set代表无序不可重复的集合。像HashSet、TreeSet
-
Queue代表队列,像双端队列ArrayQueue,优先队列PriorityQueue
-
-
Map,代表键值的键值对,HashMap、LinkedHashMap、TreeMap
-
Collection 接口相关集合类扩展
-
List 接口
-
ArrayList
-
内部实现原理:ArrayList 内部是基于数组实现的。它在内存中是一块连续的存储空间,这使得它能够快速地通过索引访问元素,时间复杂度为。例如,当你要获取列表中的第个元素时,底层直接通过计算数组下标偏移量就可以定位到该元素。
-
动态扩容机制:当向 ArrayList 中添加元素时,如果数组已满,它会自动进行扩容。通常是创建一个更大的新数组,然后将旧数组中的元素复制到新数组中。这个扩容过程会有一定的性能开销,但是 ArrayList 通过合理的扩容策略(例如,每次扩容为原来容量的 1.5 倍左右)来尽量减少这种开销对性能的影响。
-
适用场景:适用于频繁地通过索引访问元素,以及在列表尾部进行添加和删除操作的场景。比如,在一个数据报表系统中,需要存储一系列按照某种顺序排列的数据记录,并且经常需要根据索引获取特定位置的记录,这时 ArrayList 是一个很好的选择。
-
-
LinkedList
-
内部实现原理:LinkedList 是通过链表结构实现的。每个节点包含数据元素、指向前一个节点的引用和指向后一个节点的引用。这种链表结构使得它在插入和删除元素时非常高效,尤其是在列表中间进行操作时。例如,在插入一个新元素时,只需要修改相邻节点的引用即可,时间复杂度为(不考虑查找插入位置的时间)。
-
遍历方式:由于它不是基于连续的内存存储,所以通过索引访问元素相对较慢,时间复杂度为。但是它提供了双向遍历的功能,既可以从头部开始,也可以从尾部开始遍历链表,这在某些特定的应用场景下非常有用。
-
适用场景:适合于频繁地在列表中间进行插入和删除操作的场景。比如,在一个任务调度系统中,任务队列中的任务可能会根据优先级或者其他条件频繁地调整顺序,这时 LinkedList 可以更好地满足这种动态操作的需求。
-
-
-
Set 接口
-
HashSet
-
哈希原理的应用:HashSet 是基于哈希表实现的集合。它通过计算元素的哈希值来确定元素在集合中的存储位置,这使得它能够快速地判断元素是否存在于集合中,时间复杂度通常接近。例如,当向 HashSet 中添加一个元素时,首先计算该元素的哈希值,然后根据哈希值找到对应的存储位置,如果该位置没有元素或者已经存在的元素与要添加的元素相同(通过
equals
方法判断),则添加成功;否则添加失败。 -
元素的无序性和不可重复性:HashSet 中的元素是无序的,这是因为哈希表的存储位置是根据哈希值计算得到的,不同元素的哈希值可能导致它们在集合中的存储位置没有明显的顺序关系。同时,HashSet 不允许重复元素,这是由其内部的哈希冲突处理机制和
equals
方法的检查来保证的。 -
适用场景:适用于需要快速判断元素是否存在,并且不关心元素顺序的场景。比如,在一个用户权限管理系统中,需要存储用户的权限集合,当检查用户是否具有某种权限时,HashSet 可以快速地给出答案。
-
-
TreeSet
-
基于树结构的实现:TreeSet 是基于红黑树(一种自平衡二叉搜索树)实现的集合。它通过比较元素的大小(根据元素实现的
Comparable
接口或者在构造 TreeSet 时传入的Comparator
)来确定元素在树中的位置,从而保证集合中的元素是有序的。 -
排序规则和性能权衡:这种有序性使得 TreeSet 在遍历元素时能够按照一定的顺序输出,例如按照自然顺序或者自定义的比较顺序。但是,由于它是基于树结构实现的,插入和删除元素的操作时间复杂度为,相对 HashSet 在某些情况下可能会慢一些,不过它提供了有序的集合操作功能。
-
适用场景:适用于需要对元素进行排序并且保证元素唯一性的场景。比如,在一个成绩排名系统中,将学生的成绩存储在 TreeSet 中,可以方便地按照成绩高低输出学生的排名。
-
-
-
Queue 接口
-
ArrayQueue(假设你想说的是 ArrayDeque)
-
双端队列的特性:ArrayDeque 是一个双端队列,它既可以在头部添加和删除元素,也可以在尾部添加和删除元素。内部是通过循环数组实现的,这种实现方式使得它在两端进行操作时都比较高效,时间复杂度为。
-
扩容机制和边界处理:与 ArrayList 类似,当队列满时,ArrayDeque 会进行扩容。同时,它在处理循环数组的边界问题上有一套巧妙的机制,能够保证元素的正确添加和删除,并且充分利用数组空间。
-
适用场景:适用于需要在两端频繁操作的场景,比如在一个消息处理系统中,消息可以从头部进入队列进行优先处理,也可以从尾部进入队列按照顺序处理。
-
-
PriorityQueue
-
优先级队列的原理:PriorityQueue 是一个优先级队列,它根据元素的优先级来决定元素的出队顺序。内部是通过堆(通常是二叉堆)结构实现的,堆顶元素是优先级最高的元素。当插入一个新元素时,会根据优先级调整元素在堆中的位置,时间复杂度为。
-
优先级的定义和比较方式:元素的优先级是通过元素实现的
Comparable
接口或者在构造 PriorityQueue 时传入的Comparator
来定义的。例如,在一个任务调度系统中,任务可以根据紧急程度定义优先级,PriorityQueue 可以保证紧急程度高的任务先被处理。 -
适用场景:适用于需要按照一定的优先级顺序处理元素的场景,如操作系统中的进程调度、网络中的数据包处理等。
-
-
-
-
Map 接口相关集合类扩展
-
HashMap
-
哈希表的核心实现:HashMap 是基于哈希表实现的键值对集合。它通过对键进行哈希计算来确定键值对在集合中的存储位置,这使得它能够快速地进行查找、插入和删除操作,时间复杂度通常接近。例如,当要查找一个键对应的键值对时,首先计算键的哈希值,然后根据哈希值找到对应的存储位置,再通过键的比较(
equals
方法)来确定是否找到目标键值对。 -
哈希冲突的处理方式:在哈希计算过程中,可能会出现不同的键具有相同的哈希值的情况,这就是哈希冲突。HashMap 采用了链地址法来处理哈希冲突,即将具有相同哈希值的键值对存储在一个链表(在 Java 8 及以后,如果链表长度超过一定阈值,会转换为红黑树以提高性能)中。
-
适用场景:适用于需要快速查找、插入和删除键值对,并且键值对的顺序不重要的场景。比如,在一个缓存系统中,将缓存数据以键值对的形式存储在 HashMap 中,键是缓存的标识符,值是缓存的数据内容。
-
-
LinkedHashMap
-
链表与哈希表的结合:LinkedHashMap 是 HashMap 的子类,它在 HashMap 的基础上,通过一个双向链表来维护键值对的插入顺序或者访问顺序。这使得它既具有 HashMap 的高效查找性能,又能够按照一定的顺序遍历键值对。
-
顺序的维护方式和应用场景:如果是按照插入顺序维护,那么每次插入新的键值对时,会将其添加到链表的尾部;如果是按照访问顺序维护,在访问一个键值对后,会将其移动到链表的尾部。这种有序性在一些需要记录操作顺序或者数据访问顺序的场景中非常有用。例如,在一个网页浏览器的历史记录管理中,可以使用 LinkedHashMap 来存储访问过的网页链接,按照访问顺序进行存储,方便用户查看历史记录。
-
-
TreeMap
-
基于树结构的有序映射:TreeMap 是基于红黑树实现的键值对集合,它根据键的大小关系(通过键实现的
Comparable
接口或者在构造 TreeMap 时传入的Comparator
)来确定键值对在树中的位置,从而保证键值对是按照一定的顺序存储的。 -
性能特点和应用场景:TreeMap 的插入、删除和查找操作时间复杂度为,它提供了按照键的顺序进行遍历的功能,适用于需要对键值对进行排序,并且需要在有序的环境下进行查找、插入和删除操作的场景。比如,在一个字典应用中,将单词作为键,单词的解释作为值存储在 TreeMap 中,这样可以方便地按照字母顺序查找单词的解释。
-
-