文章提交

This commit is contained in:
2025-08-07 18:30:17 +08:00
parent 983f7388de
commit 21f63f000b
4 changed files with 148 additions and 0 deletions

View File

@@ -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则相反随机访问是On但在已知位置的插入删除是O(1)
LinkedList由于要存储前后节点的引用每个元素的内存开销更大ArrayList更节省内存但可能因为扩容机制造成一定的浪费。
# 实际应用场景
在实际项目中如果需要频繁随机访问元素会选择ArrayList如果需要频繁在两端添加删除元素比如实现队列和栈我会选择LinkedList

View File

@@ -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进行了优化引入了红黑树来解决上面的问题
- 性能提升: 当一个桶中的元素数量超过一定阈值的时候链表会被转换成红黑树。红黑树是一种自平衡的二叉搜索树即使在最坏的情况下它查找插入和删除操作的时间复杂度也能保持在Ologn),大大提高了性能。
- 阈值机制:
- 当桶中元素超过8个的时候链表转换为红黑树
- 当桶中元素少于6个的时候红黑树会退化回链表
- 安全性增强: 通过引入红黑树即使面对哈希冲突的攻击HashMap也能保持相对稳定的性能提高系统安全性
# 为什么选择红黑树
平衡性: 红黑树是一种近似平衡的二叉搜索树能保证最坏情况下的O(logn)的性能。
内存占用: 相比AVL树等其它平衡树红黑树的平衡条件较为宽松旋转操作更少内存占用更小。
实现更好的复杂度与性能平衡

View File

@@ -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

View File

@@ -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<K,V> synchronizedMap(Map<K,V> 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的幂次方。