Skip to content

Commit 480266e

Browse files
committed
docs: 更新文档
1 parent 06922e2 commit 480266e

19 files changed

+470
-388
lines changed
-27.9 KB
Binary file not shown.
46.1 KB
Binary file not shown.

docs/01.Java/JavaCore/面试/Java_面试_基础(二).md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ String#intern 方法的**作用**有:
468468
| 场景 | 内存/性能表现 | 优化建议 |
469469
| -------------- | ------------------------------------ | ------------------------------ |
470470
| 常量+常量 | 零运行时开销 | 无需处理 |
471-
| 单次变量+常量 | 1 次 `StringBuilder` 创建 | 可接受 |
471+
| 单次变量+常量 | 1 次 `StringBuilder` 创建 | 可接受 |
472472
| **循环内拼接** | 多次创建 `StringBuilder`(性能陷阱) | **必须显式用 `StringBuilder`** |
473473

474474
**最佳实践**
@@ -485,4 +485,4 @@ String#intern 方法的**作用**有:
485485
// ❌ 错误写法(低效)
486486
String s = "";
487487
for (String str : list) s += str; // 每次循环隐式新建 StringBuilder
488-
```
488+
```

docs/01.Java/JavaCore/面试/Java_面试_容器(一).md

Lines changed: 147 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -131,37 +131,72 @@ Comparator<Person> reverseAge =
131131
Comparator.comparingInt(Person::getAge).reversed();
132132
```
133133

134-
## List
134+
### 【中等】什么是 ConcurrentModificationException?⭐⭐
135135

136-
### 【简单】ArrayList 和 Array(数组)的区别
136+
::: info 什么是 ConcurrentModificationException
137137

138-
**ArrayList vs. 数组**
138+
:::
139139

140-
| **对比点** | **数组 (Array)** | **ArrayList** |
141-
| -------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
142-
| **长度可变性** | 固定长度,创建后无法调整大小 | 动态扩容(默认扩容 1.5 倍) |
143-
| **存储类型** | 支持基本类型(`int[]`)和对象类型 | 仅支持引用类型(基本类型需装箱,如 `Integer`|
144-
| **内存占用** | 更紧凑(无额外对象开销) | 有额外内存开销(记录大小、扩容预留空间等) |
145-
| **访问方式** | 通过索引直接访问(`arr[0]`| 通过 `get(index)`/`set(index)` 方法访问 |
146-
| **操作效率** | - 查询:O(1)(极快)<br>- 增删:O(n)(需移动元素) | - 查询:O(1)(底层是数组)<br>- 增删:<br> - 尾部操作:O(1)<br> - 中间操作:O(n)(需移动元素) |
147-
| **功能方法** | 功能简单(依赖 `Arrays` 工具类) | 提供丰富方法(`add()``remove()``contains()` 等) |
148-
| **线程安全** | 非线程安全 | 非线程安全(需用 `Collections.synchronizedList` 包装) |
149-
| **泛型支持** | 不支持泛型(类型检查在运行时) | 支持泛型(编译时类型安全) |
140+
`ConcurrentModificationException` 是在 Java 中使用**迭代器**遍历集合时,如果检测到集合被**意外修改**而抛出的运行时异常。
150141

151-
**小结**
142+
`ConcurrentModificationException` 底层机制
152143

153-
- **动态性**`ArrayList` 自动扩容,数组长度固定。
154-
- **类型支持**:数组可直接存基本类型,`ArrayList` 需包装类。
155-
- **性能**
156-
- 数组的随机访问稍快(少一次方法调用)。
157-
- `ArrayList` 的尾部插入高效,但中间插入/删除需移动元素。
158-
- **功能**`ArrayList` 提供更多便捷方法(如迭代、搜索)。
159-
- **内存**:数组更节省内存,`ArrayList` 有额外结构开销。
144+
迭代器内部维护了一个计数器 **`expectedModCount`**(期望修改次数),与集合的 **`modCount`**(实际修改次数)比较。每次修改集合时 `modCount++`,迭代器每次操作检查两者是否一致,不一致立即抛出异常。
160145

161-
**应用**
146+
::: info 什么时候发生 ConcurrentModificationException?
162147

163-
- **选数组**:需极致性能、固定长度或存储基本类型时(如数学计算)。
164-
- **选 ArrayList**:需要动态大小、便捷操作或泛型安全时(大多数业务场景)。
148+
:::
149+
150+
迭代器创建后,集合被**非迭代器方式**修改结构(增删),就会抛出 `ConcurrentModificationException`
151+
152+
```java
153+
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
154+
155+
// 错误示例 1:遍历时直接修改集合
156+
for (String item : list) { // 底层使用迭代器
157+
if ("B".equals(item)) {
158+
list.remove(item); // 抛出 ConcurrentModificationException
159+
}
160+
}
161+
162+
// 错误示例 2:迭代器创建后通过其他方式修改
163+
Iterator<String> it = list.iterator();
164+
list.add("D"); // 结构被修改
165+
it.next(); // 此处抛出异常
166+
```
167+
168+
::: info 如何避免 ConcurrentModificationException?
169+
170+
:::
171+
172+
- 单线程优先使用 `removeIf()``Iterator.remove()`
173+
- 多线程必须使用并发集合或显式同步
174+
175+
【示例】使用迭代器删除元素(标准方式)
176+
177+
```java
178+
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));
179+
180+
Iterator<String> iterator = list.iterator();
181+
while (iterator.hasNext()) {
182+
String item = iterator.next();
183+
if ("B".equals(item) || "C".equals(item)) {
184+
iterator.remove(); // ✅ 通过迭代器安全删除
185+
}
186+
}
187+
// list = ["A", "D"]
188+
```
189+
190+
【示例】Java 8+ 的 removeIf(最简洁)
191+
192+
```java
193+
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));
194+
// 单行完成过滤
195+
list.removeIf(item -> item.startsWith("B") || item.equals("C")); //
196+
// list = ["A", "D"]
197+
```
198+
199+
## List
165200

166201
### 【简单】ArrayList 可以添加 null 值吗?
167202

@@ -188,6 +223,66 @@ Comparator<Person> reverseAge =
188223
- 明确是否需要 `null`,避免滥用导致代码健壮性问题。
189224
- 必要时用 `Optional` 或默认值替代 `null`
190225

226+
### 【简单】ArrayList 如何扩容?⭐⭐⭐
227+
228+
ArrayList 默认初始大小为 10,当元素数达到容量时,触发扩容,每次扩容 1.5 倍。
229+
230+
扩容关键代码:
231+
232+
```java
233+
private void grow(int minCapacity) { // minCapacity = 当前size + 1
234+
int oldCapacity = elementData.length;
235+
236+
// 关键:新容量 = 旧容量 + 旧容量的一半(1.5倍扩容)
237+
int newCapacity = oldCapacity + (oldCapacity >> 1);
238+
239+
// 特殊情况处理
240+
if (newCapacity - minCapacity < 0) // 新容量仍不够
241+
newCapacity = minCapacity; // 直接使用所需容量
242+
if (newCapacity - MAX_ARRAY_SIZE > 0) // 超过最大限制
243+
newCapacity = hugeCapacity(minCapacity);
244+
245+
// 核心:创建新数组并拷贝数据
246+
elementData = Arrays.copyOf(elementData, newCapacity);
247+
}
248+
```
249+
250+
因此,为了避免频繁扩容,推荐根据实际情况预分配容量。
251+
252+
```java
253+
ArrayList<String> list = new ArrayList<>(10000);
254+
```
255+
256+
### 【简单】ArrayList 和数组有什么区别?
257+
258+
**ArrayList vs. 数组**
259+
260+
| **对比点** | **数组 (Array)** | **ArrayList** |
261+
| -------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
262+
| **长度可变性** | 固定长度,创建后无法调整大小 | 动态扩容(默认扩容 1.5 倍) |
263+
| **存储类型** | 支持基本类型(`int[]`)和对象类型 | 仅支持引用类型(基本类型需装箱,如 `Integer`|
264+
| **内存占用** | 更紧凑(无额外对象开销) | 有额外内存开销(记录大小、扩容预留空间等) |
265+
| **访问方式** | 通过索引直接访问(`arr[0]`| 通过 `get(index)`/`set(index)` 方法访问 |
266+
| **操作效率** | - 查询:O(1)(极快)<br>- 增删:O(n)(需移动元素) | - 查询:O(1)(底层是数组)<br>- 增删:<br> - 尾部操作:O(1)<br> - 中间操作:O(n)(需移动元素) |
267+
| **功能方法** | 功能简单(依赖 `Arrays` 工具类) | 提供丰富方法(`add()``remove()``contains()` 等) |
268+
| **线程安全** | 非线程安全 | 非线程安全(需用 `Collections.synchronizedList` 包装) |
269+
| **泛型支持** | 不支持泛型(类型检查在运行时) | 支持泛型(编译时类型安全) |
270+
271+
**小结**
272+
273+
- **动态性**`ArrayList` 自动扩容,数组长度固定。
274+
- **类型支持**:数组可直接存基本类型,`ArrayList` 需包装类。
275+
- **性能**
276+
- 数组的随机访问稍快(少一次方法调用)。
277+
- `ArrayList` 的尾部插入高效,但中间插入/删除需移动元素。
278+
- **功能**`ArrayList` 提供更多便捷方法(如迭代、搜索)。
279+
- **内存**:数组更节省内存,`ArrayList` 有额外结构开销。
280+
281+
**应用**
282+
283+
- **选数组**:需极致性能、固定长度或存储基本类型时(如数学计算)。
284+
- **选 ArrayList**:需要动态大小、便捷操作或泛型安全时(大多数业务场景)。
285+
191286
### 【简单】ArrayList 和 LinkedList 有什么区别?
192287

193288
**`ArrayList` vs. `LinkedList`**
@@ -220,19 +315,32 @@ List` 需遍历链表。
220315
> - 默认情况下,`Collections.synchronizedList` 包装的 `ArrayList``LinkedList` 线程安全开销更低。
221316
> - Java 8+ 的 `Stream` 操作在 `ArrayList` 上效率更高。
222317
318+
### 【中等】CopyOnWriteArrayList 的原理是什么?⭐⭐
319+
320+
`CopyOnWriteArrayList` 核心思想是 "写时复制"(Copy-On-Write,CoW),**适用于【读多写少】的高并发场景**
321+
322+
`CopyOnWriteArrayList` 内部维护一个 `volatile` Object 数组(`Object[] array`),存储所有元素,`volatile` 保证了并发可见性
323+
324+
- 读操作:由于读取数据是 volatile,保证了并发可见性,所以无需加锁
325+
- 写操作(add/set/remove):核心步骤如下
326+
1. 加锁(`ReentrantLock`
327+
2. `Arrays.copyOf` 复制新数组(长度 ±1)
328+
3. 在新数组上修改
329+
4. 替换原数组引用
330+
223331
## Set
224332

225333
### 【简单】HashSet、LinkedHashSet 和 TreeSet 有什么区别?
226334

227-
| 特性 | HashSet | LinkedHashSet | TreeSet |
228-
| ---------------- | -------------------- | ----------------------- | -------------------------------- |
229-
| **底层实现** | 哈希表 (HashMap) | 哈希表 + 链表 | 红黑树 |
230-
| **排序保证** | 无顺序 | 插入顺序 | 自然顺序/自定义排序 |
231-
| **时间复杂度** | 添加/删除/查找:O(1) | 添加/删除/查找:O(1) | 添加/删除/查找:O(log n) |
232-
| **允许 null 元素** | 允许 1 个 null | 允许 1 个 null | 不允许(除非自定义 Comparator 允许) |
233-
| **线程安全** | 非线程安全 | 非线程安全 | 非线程安全 |
234-
| **性能特点** | 最快的基础操作 | 比 HashSet 稍慢但保持顺序 | 最慢但自动排序 |
235-
| **使用场景** | 只需唯一性不关心顺序 | 需要保持插入顺序 | 需要排序的集合 |
335+
| 特性 | HashSet | LinkedHashSet | TreeSet |
336+
| ------------------ | -------------------- | ------------------------- | ------------------------------------ |
337+
| **底层实现** | 哈希表 (HashMap) | 哈希表 + 链表 | 红黑树 |
338+
| **排序保证** | 无顺序 | 插入顺序 | 自然顺序/自定义排序 |
339+
| **时间复杂度** | 添加/删除/查找:O(1) | 添加/删除/查找:O(1) | 添加/删除/查找:O(log n) |
340+
| **允许 null 元素** | 允许 1 个 null | 允许 1 个 null | 不允许(除非自定义 Comparator 允许) |
341+
| **线程安全** | 非线程安全 | 非线程安全 | 非线程安全 |
342+
| **性能特点** | 最快的基础操作 | 比 HashSet 稍慢但保持顺序 | 最慢但自动排序 |
343+
| **使用场景** | 只需唯一性不关心顺序 | 需要保持插入顺序 | 需要排序的集合 |
236344

237345
**顺序特性**
238346

@@ -302,13 +410,13 @@ Set<String> customTreeSet = new TreeSet<>(Comparator.reverseOrder());
302410
::: info Queue vs. Deque
303411
:::
304412

305-
| 特性 | Queue (队列) | Deque (双端队列) |
306-
| ------------ | ------------------------------------------ | ----------------------------- |
307-
| **进出原则** | 先进先出 (FIFO) | 两端都可进出 (FIFO + LIFO) |
413+
| 特性 | Queue (队列) | Deque (双端队列) |
414+
| ------------ | -------------------------------------------- | ----------------------------- |
415+
| **进出原则** | 先进先出 (FIFO) | 两端都可进出 (FIFO + LIFO) |
308416
| **主要操作** | 队尾入队 (add/offer),队首出队 (remove/poll) | 支持队首/队尾的入队和出队操作 |
309-
| **继承关系** | 基础接口 | 继承自 Queue 接口 |
310-
| **代表子类** | LinkedList, PriorityQueue | ArrayDeque, LinkedList |
311-
| **特殊功能** | - | 支持栈操作 (push/pop/peek) |
417+
| **继承关系** | 基础接口 | 继承自 Queue 接口 |
418+
| **代表子类** | LinkedList, PriorityQueue | ArrayDeque, LinkedList |
419+
| **特殊功能** | - | 支持栈操作 (push/pop/peek) |
312420

313421
**基本操作对比**
314422

0 commit comments

Comments
 (0)