Java集合框架体系

随笔3个月前发布 萍水相连
47 0 0

Java集合框架体系

Java集合框架体系

Java集合类主要由两个接口Collection和Map派生出来的,Collection有三个子接口:List、Set、Queue。

Collection:最基本的集合接口,代表一组元素的集合。

List:代表有序的、可重复的元素。
Set:代表不可重复的的集合。
Queue: 代表队列

Map:存储键值对的集合,键不允许重复。

List 、Set、Queue、Map的区别

List:

基于索引的集合,可以包含重复的元素。
支持元素的有序存储,即元素插入的顺序就是它们在列表中的顺序。
允许对元素进行快速随机访问。
常见的实现类有 ArrayList、LinkedList

Set:

不包含重复元素的集合。
通常不保证元素的顺序,或者保证元素按照某种规则排序(如 TreeSet)。
HashSet 是基于哈希表实现的,不保证元素顺序;LinkedHashSet 维护元素插入的顺序;TreeSet 则根据元素的自然顺序或构造时提供的比较器对元素进行排序。
常见的实现类有 HashSet、LinkedHashSet 和 TreeSet。

Queue:

队列是一种特殊的集合,主要用于维护元素的顺序。通常按照先进先出(FIFO)的原则进行元素的插入和移除。
支持元素的插入和移除操作,如 offer、poll、peek 等
常见的实现类有LinkedList(先进先出FIFO)、PriorityQueue(优先队列)和 ArrayDeque。

Map

存储键值对的集合,每个键最多只能映射到一个值。
键必须唯一,而值可以重复。
不保证元素的顺序,或者可以按照键或值的某种规则排序(如 TreeMap)。
常见的实现类有 HashMap、LinkedHashMap、TreeMap

ArrayList与LinkedList的区别

1.底层数据结构:ArrayList基于动态数组实现;LinkedList基于(双向)链表实现。
2.随机索引访问:ArrayList的速度要优于LinkedList。因为ArrayList直接通过数组下标直接找到元素;LinkedList要移动指针遍历每个元素直到找到为止。
3.新增和删除元素:LinkedList的速度要优于ArrayList。因为ArrayList在新增和删除元素时,可能移动其它元素、扩容和复制数组;LinkedList除了实例化对象需要时间外,只需要修改指针即可。(LinkedList新增删除元素比ArrayList快的两个前提:1 往集合中间插入数据时ArrayList比linkedList慢 2 ArrayList正好扩容的时候添加数据要比LinkedList慢)
4.内存占用:ArrayList占用比LinkedList小,因为LinkedList的每个元素都需要存储额外的指针(prev 和 next)
5.线程安全:ArrayList与LinkedList都是非同步的,说明是非线程安全的,

特性 ArrayList LinkedList
内部结构 动态数组(基于数组实现) 双向链表(基于链表实现)
随机访问 快(O(1) 时间复杂度) 慢(O(n) 时间复杂度,需要遍历链表)
添加/删除 可能慢(特别是需要扩容时) 快(O(1) 时间复杂度,但在链表中间较慢)
内存占用 相对较小 相对较大(每个元素都需要额外的内存来存储指针)
扩容机制 当数组容量不足时自动扩容(JDK8,1.5倍) 不需要扩容
迭代器 快速且轻量级 相对较慢且占用更多内存
适用场景 频繁的随机访问 频繁的插入和删除操作

Arraylist 和 Vector 的区别

ArrayList在内存不够时,默认是1.5被扩展,Vector是默认扩展1倍。
ArrayList是非同步的。Vector是同步的,属于线程安全级别的。由于 Vector 的同步机制,它在性能上通常比 ArrayList 慢。
通常建议使用 ArrayList,因为它提供了更好的性能,并且在需要线程安全时,可以通过Collections.synchronizedList() 方法或使用 CopyOnWriteArrayList 类来实现。

HashMap

HashMap是一个散列表,它存储的内容是键值对(key-value)映射,根据键的HashCode值存储数据。HashMap 使用数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的, 链表长度大于8(TREEIFY_THRESHOLD)时,会把链表转换为红黑树,红黑树节点个数小于6(UNTREEIFY_THRESHOLD)时才转化为链表,防止频繁的转化。

Java集合框架体系

哈希表

哈希表也称为散列表,是一种通过哈希函数将键(Key)映射到表中一个位置以便快速访问记录的数据结构。

hash算法

在HashMap 实现中,hash算法是用来计算键的哈希码并将其映射到内部数组的索引上。以下是其核心:

第一步,获取key的hash

h = key.hashCode()

第二步,计算hashmap的hash

hash =(h = key.hashCode()) ^ (h >>> 16)

第三步,计算槽位(数组下标)。n是Node<K,V>[] table哈希桶数组的长度。

i = (n – 1) & hash

示例:
hashcode是int型。提前了解,十进制转二进制,位运算符。

假设
第一步:h=10,
第二步:hash=10^(10>>>16)。

>>>是无符号右移运算。10的二进制是0000000000000000 0000000000001010。那么10>>>16,右移16位意味着丢弃最低的16位,结果是0。
^是异或运算,10^0结果还是10。

第三步:i = (n – 1) & hash

&是与运算。n默认是16。i=(16-1)&10结果是10。

因为第二步是取hashcode丢弃最低的16位的结果与hashcode进行按位异或运算,所以又被称为高16位运算
因为10&(16-1)=10%16,所以第三步又被称为取模运算
计算机中直接求余效率不如位移运算,hash%length==hash&(length-1)的前提是length是2的n次方,能均匀分布减少碰撞。

源码解析可以查看HashMap的put方法

扩容机制(请阅读HashMap的resize方法)

数组扩容
当hashmap中的元素超过阈值(当前容量与负载因子的乘积),触发扩容。
通过创建一个2倍当前容量的新数组,替换原数组来完成扩容。
在此过程中需要重新计算每个键的哈希值,键值对需要重新映射

链表扩容
当链表长度超过一定阈值(TREEIFY_THRESHOLD,默认为8)时,链表会被转换为红黑树。
当红黑树节点个数小于6(UNTREEIFY_THRESHOLD)时,红黑树会被转化为链表。

红黑树

红黑树是一种自平衡的二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。它具有以下特性:

1.节点颜色:每个节点要么是红色,要么是黑色。
2.根节点:根节点是黑色的。
3.叶子节点:所有叶子节点(NIL节点,空节点)都是黑色的。
4.红色节点的子节点:每个红色节点的两个子节点都是黑色的(不能有两个连续的红色节点)。
5.路径黑色节点数:从根节点到任意叶子节点的路径上,黑色节点的数量相同。

可结合HashMap里的TreeNode类理解。

HashMap和HashTable的区别

HashMap 和 Hashtable 都是 Java 中用于存储键值对的集合类,以下是它们的一些区别:

继承关系:

Hashtable 继承自 Dictionary 类,而 Dictionary 类是 Java 的早期版本中的一个类,现在已经不推荐使用
HashMap 继承自 AbstractMap 类

线程安全性:

Hashtable 是线程安全的,它的所有公共方法都是同步的,这意味着在多线程环境中,Hashtable 可以安全地使用而不需要额外的同步措施。
HashMap 是非线程安全的,如果在多线程环境中使用,需要外部同步或者使用 Collections.synchronizedMap 方法来包装它。

null 键和值:

Hashtable 不允许使用 null 作为键或值。
HashMap 允许使用一个 null 键和多个 null 值。

在实际应用中,由于 HashMap 提供了更好的性能和更灵活的使用方式,它通常是首选的 Map 实现。如果需要线程安全的 Map 实现,可以使用 ConcurrentHashMap,它是 Java 并发包中的一个线程安全 Map 实现,或者使用 Collections.synchronizedMap 方法来包装一个 HashMap。

HashMap与ConcurrentHashMap的区别

ConcurrentHashMap 和 HashMap 都用于存储键值对,以下是它们的以下区别:

线程安全性

ConcurrentHashMap 是线程安全的
HashMap 是非线程安全的

HashSet

HashSet 实现了 Set 接口,它不允许集合中有重复的元素,并且不保证集合的迭代顺序;

以下是 HashSet 的一些关键特性:

不允许重复

HashSet 保证集合中每个元素都是唯一的。尝试添加已存在的元素将不会被添加。

无序

HashSet 不保证元素的顺序,这意味着每次迭代 HashSet 时元素的顺序可能不同。

基于哈希表

HashSet 内部使用哈希表实现,这使得添加、删除和查找元素的操作通常具有常数时间复杂度(即 O(1))。

非同步

HashSet 是非线程安全的。在多线程环境中,如果需要线程安全,可以使用 Collections.synchronizedSet 方法将其包装,或者使用 ConcurrentHashMap 作为替代。

当需要迭代顺序一致时,不应该使用 HashSet,而应该使用 LinkedHashSet。

TreeSet

TreeSet 实现了 Set 接口,它不允许集合中有重复的元素,并且保证了元素处于排序状态。TreeSet 基于红黑树(一种自平衡的二叉搜索树)实现,这使得它能够提供有序的数据集合。

以下是 TreeSet 的一些关键特性和用法:

不允许重复

TreeSet 不允许插入重复的元素。如果尝试添加已存在的元素,该元素不会被添加。

有序

TreeSet 保证了元素的迭代顺序是有序的,这使得它适合需要有序数据的场景。

性能:

TreeSet 的查找、插入和删除操作通常具有 O(log n) 的时间复杂度,其中 n 是集合中的元素数量。

线程安全性:

TreeSet 是非线程安全的。在多线程环境中,如果需要线程安全,可以使用 Collections.synchronizedSortedSet 方法将其包装,或者使用 ConcurrentSkipListSet。

TreeMap

TreeMap 实现了 Map 接口,它存储键值对并保证了键处于排序状态。TreeMap 基于红黑树(一种自平衡的二叉搜索树)实现,这使得它能够提供有序的数据集合。

以下是 TreeMap 的一些关键特性和用法:

不允许重复键

TreeMap 不允许插入重复的键。如果尝试添加已存在的键,那么旧的值将被新的值替换。

有序

TreeMap 保证了键处于有序状态,并且保证了元素的迭代顺序是有序的,这使得它适合需要有序数据的场景。

性能:

TreeMap 的查找、插入和删除操作通常具有 O(log n) 的时间复杂度,其中 n 是映射中的元素数量。

线程安全性:

TreeMap 是非线程安全的。在多线程环境中,如果需要线程安全,可以使用 Collections.synchronizedMap 方法将其包装。

参考

Java集合框架体系
Java集合面试题
Java常见集合总结
Java集合框架全面解析
菜鸟教程:Java集合
java集合框架综述
ArrayList 和 LinkedList 的真正区别
Java 8的 HashMap 设计与实现
HashMap理解
深入理解HashMap(四): 关键源码逐行分析之resize扩容

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...