diff --git a/src/content/posts/Java/集合/ArrayList和LinkedList的区别.md b/src/content/posts/Java/集合/ArrayList和LinkedList的区别.md new file mode 100644 index 0000000..3b1905e --- /dev/null +++ b/src/content/posts/Java/集合/ArrayList和LinkedList的区别.md @@ -0,0 +1,25 @@ +--- +title: ArrayList和LinkedList的区别 +published: 2025-08-06 +description: '' +image: '' +tags: ['ArrayList', 'LinkedList'] +category: 'Java > 集合框架' +draft: false +lang: '' +--- + + +# Java ArrayList和LinkedList的区别 + +ArrayList基于动态数组实现,LinkedList基于双向链表实现,这是它们所有性能差异的根本原因 + +ArrayList随机访问是O(1),但是中间插入是O(n),LinkedList则相反,随机访问是O(n),但在已知位置的插入删除是O(1) + +LinkedList由于要存储前后节点的引用,每个元素的内存开销更大,ArrayList更节省内存,但可能因为扩容机制造成一定的浪费。 + +# 实际应用场景 + +在实际项目中,如果需要频繁随机访问元素,会选择ArrayList,如果需要频繁在两端添加删除元素,比如实现队列和栈,我会选择LinkedList + + diff --git a/src/content/posts/Java/集合/JavaHashMap为什么在jdk8引入红黑树.md b/src/content/posts/Java/集合/JavaHashMap为什么在jdk8引入红黑树.md new file mode 100644 index 0000000..6d26658 --- /dev/null +++ b/src/content/posts/Java/集合/JavaHashMap为什么在jdk8引入红黑树.md @@ -0,0 +1,40 @@ +--- +title: Java HashMap为什么在jdk8引入红黑树 +published: 2025-08-05 +description: '' +image: '' +tags: ['红黑树','HashMap'] +category: 'Java > 集合框架' +draft: false +lang: '' +--- + + +# Java HashMap为什么在jdk8引入红黑树 + +在JDK8之前,HashMap的内部实现主要依赖于数组+链表的结构 +当多个元素的哈希值相同的时候(也就是发生哈希冲突的时候),这些元素会被存储在同一个桶里面,形成一个链表。 +但这种实现方式在特定情况下会导致性能问题。 + +# JDK8之前的问题 + +1. 时间复杂度退化: 在最坏情况下(大量元素哈希到同一个桶),查找,插入和删除操作的时间复杂度会从理想的O(1)退化为O(n),其中n是链表的长度 +2. 哈希冲突攻击: 恶意攻击者可以构造大量哈希冲突的数据,使得HashMap的性能急剧下降,导致潜在的拒接服务攻击。 + +# 红黑树的引入 +JDK8对HashMap进行了优化,引入了红黑树来解决上面的问题: +- 性能提升: 当一个桶中的元素数量超过一定阈值的时候,链表会被转换成红黑树。红黑树是一种自平衡的二叉搜索树,即使在最坏的情况下,它查找,插入和删除操作的时间复杂度也能保持在O(logn),大大提高了性能。 + +- 阈值机制: + - 当桶中元素超过8个的时候,链表转换为红黑树 + - 当桶中元素少于6个的时候,红黑树会退化回链表 + +- 安全性增强: 通过引入红黑树,即使面对哈希冲突的攻击,HashMap也能保持相对稳定的性能,提高系统安全性 + + +# 为什么选择红黑树 +平衡性: 红黑树是一种近似平衡的二叉搜索树,能保证最坏情况下的O(logn)的性能。 + +内存占用: 相比AVL树等其它平衡树,红黑树的平衡条件较为宽松,旋转操作更少,内存占用更小。 + +实现更好的复杂度与性能平衡 diff --git a/src/content/posts/Java/集合/Java迭代器Iterator和Iterable.md b/src/content/posts/Java/集合/Java迭代器Iterator和Iterable.md new file mode 100644 index 0000000..4e6c5a5 --- /dev/null +++ b/src/content/posts/Java/集合/Java迭代器Iterator和Iterable.md @@ -0,0 +1,14 @@ +--- +title: Java迭代器Iterator和Iterable +published: 2025-08-05 +description: '' +image: '' +tags: [Iterator,Iterable] +category: 'Java > 集合框架' +draft: false +lang: '' +--- + +# Java迭代器Iterator和Iterable + + diff --git a/src/content/posts/Java/集合/concurrenthashmap的实现原理.md b/src/content/posts/Java/集合/concurrenthashmap的实现原理.md new file mode 100644 index 0000000..2dd326c --- /dev/null +++ b/src/content/posts/Java/集合/concurrenthashmap的实现原理.md @@ -0,0 +1,69 @@ +--- +title: concurrenthashmap的实现原理 +published: 2025-08-06 +description: '' +image: '' +tags: [ConcurrentHashMap,Java] +category: 'Java > 集合框架' +draft: false +lang: '' +--- + + +# ConcurrentHashMap实现原理 + +ConcurrentHashMap是Java并发包中一种线程安全的哈希表实现。 +HashMap在多线程环境下扩容会出现CPU接近100%的情况,因为HashMap并不是线程安全的,我们可以通过Collections里面的Map synchronizedMap(Map m) 把HashMap包装成一个线程安全的map + +比如SynchronizedMap的put方法就是加锁过的 + + +# ConcurrentHashMap的变化 +ConcurrentHashMap在JDK1.7中,提供了一种粒度更细的加锁机制,这种机制叫分段锁,整个哈希表被分为多个段,每个段都独立锁定。读取操作不需要锁,写入操作仅锁定相关的段,这减小了锁冲突的几率,提高了并发性能。 + +这种机制的优点是: 在并发环境下将实现更高的吞吐量,在单线程环境下只损失非常小的性能。 + +可以这样理解分段锁,就是将数据分段,对每一段数据分配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。 + +有些方法需要跨段,比如size(),isEmpty(),containsValue(),它们可能需要锁定整个表而不仅仅是某个段,这需要按顺序锁定所有段,操作完以后,再按顺序释放所有段的锁。 + + +ConcurrentHashMap是由Segment数组结构和HashEntry构成的,Segment是一种可重入的锁,HashEntry则用于存储键值对数据。 + +一个ConcurrentHashMap里面包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构,一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里的元素,当HashEntry数组的数据进行修改的时候,必须首先获得它对应的Segment锁。 + +在外部:有一个 Segment 数组,作为并发控制的“总入口”,每个 Segment 都是一个独立的锁喵~ +在内部:每个 Segment 自己就是一个完整的小型 HashMap!它有自己的哈希表数组,里面的每个桶都可以通过 next 指针挂着一个或多个 Entry 组成的链表. + + + +# ConcurrentHashMap 读写过程 + +## get方法 +- 为输入的key做hash运算,得到hash值 +- 通过Hash值,定位到对应的Segment对象 +- 再次通过hash值,定位到Segment当中数组的具体位置 + + +## put方法 +- 为输入的key做hash运算,得到hash值 +- 通过hash值,定位到对应的Segment对象 +- 获取可重入锁 +- 再次通过hash值,定位到Segment当中数组的具体位置 +- 插入或者覆盖HashEntry对象 +- 释放锁 + + +# JDK1.8 +在JDK1.8中,ConcurrentHashMap主要做了两个优化: +- 和HashMap一样,链表也会在长度到达8的时候转换为红黑树,这样可以提升大量冲突的时候的查询效率。 +- 以某个位置的头结点为锁,配合自旋 + CAS 避免不必要的锁开销,进一步提升并发性能。 +- 相比JDK1.7中的ConcurrentHashMap,JDK1.8的ConcurrentHashMap取消了Segment分段锁,采用CAS + synchronized来保证并发安全性。整个容器只分为一个Segment,也就是table数组。 +- JDK1.8中的ConcurrentHashMap对节点Node类中的共享变量,和JDK1.7一样,使用volatile关键字,保证多线程操作的时候,变量的可见性。 + + +# ConcurrentHashMap的字段 +1. table +这个装载Node的数组,作为ConcurrentHashMap的底层容器,采用加载的方式,直到第一次插入数据的时候才会进行初始化操作 +数组的大小是2的幂次方。 +