update
This commit is contained in:
117
java/包装模式之java对象.md
Normal file
117
java/包装模式之java对象.md
Normal file
@@ -0,0 +1,117 @@
|
||||
## 当引用第三方包的java对象时重写equals
|
||||
|
||||
使用包装模式
|
||||
```
|
||||
|
||||
private static class VideoInfoWrapper {
|
||||
|
||||
private final VideoInfo videoInfo;
|
||||
|
||||
public VideoInfoWrapper(VideoInfo videoInfo) {
|
||||
this.videoInfo = videoInfo;
|
||||
}
|
||||
|
||||
public VideoInfo unwrap() {
|
||||
return videoInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof VideoInfo)) {
|
||||
return false;
|
||||
}
|
||||
VideoInfo vi = (VideoInfo) obj;
|
||||
return videoInfo.id.equals(vi.id)
|
||||
&& videoInfo.width == vi.width
|
||||
&& videoInfo.height == vi.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int n = 31;
|
||||
n = n * 31 + videoInfo.id.hashCode();
|
||||
n = n * 31 + videoInfo.height;
|
||||
n = n * 31 + videoInfo.width;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
自定义过滤函数实现去重
|
||||
|
||||
|
||||
|
||||
```
|
||||
private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
|
||||
Map<Object, Boolean> map = new ConcurrentHashMap<>();
|
||||
return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
|
||||
}
|
||||
```
|
||||
|
||||
要理解 `distinctByKey` 方法中 `map` 的生命周期及其被捕获的原因,需要结合 Java 的**变量作用域**、**lambda 表达式的闭包特性**以及**流的执行机制**来分析。
|
||||
|
||||
### 一、`map` 的生命周期细节
|
||||
|
||||
`map` 是 `distinctByKey` 方法内部创建的局部变量,其生命周期可分为以下阶段:
|
||||
|
||||
#### 1. **创建阶段:方法调用时初始化**
|
||||
|
||||
当调用 `distinctByKey(keyExtractor)` 方法时,方法体首先执行 `new ConcurrentHashMap<>()`,此时 `map` 被创建并初始化(分配内存、初始化哈希表等)。
|
||||
|
||||
此时 `map` 的作用域仅限于 `distinctByKey` 方法内部,是一个**局部变量**。
|
||||
|
||||
#### 2. **存活阶段:被 lambda 捕获,随流处理存活**
|
||||
|
||||
方法返回的 `Predicate<T>` 是一个 lambda 表达式:`t -> map.putIfAbsent(...)`。
|
||||
|
||||
这个 lambda 表达式**捕获了方法内部的 `map` 变量**(即持有对 `map` 的引用),因此即使 `distinctByKey` 方法执行结束,`map` 也不会被立即销毁 —— 因为它被外部的 `Predicate` 对象引用着。
|
||||
|
||||
当这个 `Predicate` 被传递给流的 `filter` 操作(如 `stream.filter(distinctByKey(...))`)时:
|
||||
|
||||
- 流的中间操作(如 `filter`)仅会保存 `Predicate` 对象,不会立即执行。
|
||||
- 直到流的**终端操作**(如 `collect`、`forEach`)被调用时,流才开始处理元素,此时 `Predicate.test(t)` 会被反复调用,每次调用都会通过捕获的引用操作同一个 `map`(记录已出现的键,实现去重)。
|
||||
|
||||
因此,`map` 在整个流处理期间(从终端操作开始到结束)一直存活,用于存储去重的状态。
|
||||
|
||||
#### 3. **销毁阶段:引用链断裂后被回收**
|
||||
|
||||
当流的终端操作执行完毕,流处理结束后:
|
||||
|
||||
- 如果 `Predicate` 对象没有被其他地方引用(通常是一次性使用,用完即弃),则 `Predicate` 会成为垃圾回收(GC)的候选对象。
|
||||
- 由于 `map` 仅被这个 `Predicate` 引用,当 `Predicate` 被 GC 回收时,`map` 的引用计数变为 0,也会被 GC 回收。
|
||||
|
||||
至此,`map` 的生命周期结束。
|
||||
|
||||
### 二、为什么 `map` 会被 lambda 捕获?
|
||||
|
||||
lambda 表达式(如 `Predicate` 的实现)本质是**闭包(Closure)**—— 它可以访问外部作用域(即 `distinctByKey` 方法)中的变量,这种访问通过 “捕获变量” 实现。具体原因如下:
|
||||
|
||||
#### 1. 功能需求:需要共享状态
|
||||
|
||||
`distinctByKey` 的核心逻辑是 “通过记录已出现的键实现去重”,因此需要一个共享的容器(`map`)来存储这些键。
|
||||
|
||||
如果 `map` 不被 lambda 捕获,那么每次调用 `Predicate.test(t)` 时都会创建新的 `map`,无法记录历史状态,去重功能就会失效。
|
||||
|
||||
#### 2. Java 语法对闭包的支持
|
||||
|
||||
Java 的 lambda 表达式可以捕获外部作用域的变量,但有严格限制:**只能捕获 “有效最终变量(effectively final)”**(即变量声明后从未被修改,或被 `final` 修饰)。
|
||||
|
||||
在 `distinctByKey` 中:
|
||||
|
||||
- `map` 被声明为 `Map<Object, Boolean> map = new ConcurrentHashMap<>()`,且后续从未被重新赋值(始终指向同一个 `ConcurrentHashMap` 实例),因此它是 “有效最终变量”。
|
||||
- 因此,lambda 表达式可以合法地捕获 `map` 的引用(注意:捕获的是引用,不是变量本身),并在后续操作中通过该引用修改 `map` 内部的内容(如 `putIfAbsent`)。
|
||||
|
||||
#### 3. 捕获的本质:延长变量生命周期
|
||||
|
||||
从内存角度看,`map` 原本是 `distinctByKey` 方法的局部变量,方法执行结束后本应被销毁。但由于 lambda 表达式(`Predicate`)持有对 `map` 的引用,`map` 的生命周期被延长,与 `Predicate` 的生命周期绑定 —— 只要 `Predicate` 还存在,`map` 就不会被回收。
|
||||
|
||||
### 三、关键结论
|
||||
|
||||
- **`map` 的生命周期**:从 `distinctByKey` 方法调用时创建,到流处理结束且 `Predicate` 被回收时销毁,贯穿整个流的去重过程。
|
||||
- **被捕获的原因**:lambda 表达式需要通过共享 `map` 记录去重状态,而 Java 允许 lambda 捕获 “有效最终变量”,从而实现闭包对外部状态的访问。
|
||||
|
||||
这种设计使得 `distinctByKey` 方法能简洁地实现 “按键去重”,但需注意:同一个 `Predicate` 实例不能重复用于多个流,否则 `map` 会累积多个流的状态,导致逻辑错误。
|
||||
4
java/深浅拷贝.md
Normal file
4
java/深浅拷贝.md
Normal file
@@ -0,0 +1,4 @@
|
||||
Apache BeanUtils还是Spring BeanUtils MapStruct m默认都是浅拷贝
|
||||
|
||||
|
||||
java 8大基础数据类型的包装类都是不可变,不受深浅拷贝影响
|
||||
20
kms.md
Normal file
20
kms.md
Normal file
@@ -0,0 +1,20 @@
|
||||
slmgr /ipk TVRH6-WHNXV-R9WG3-9XRFY-MY832
|
||||
|
||||
slmgr /skms kms.03k.org
|
||||
|
||||
slmgr /ato
|
||||
|
||||
slmgr /xpr
|
||||
|
||||
slmgr /dlv
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2025 标准版
|
||||
TVRH6-WHNXV-R9WG3-9XRFY-MY832
|
||||
|
||||
|
||||
|
||||
https://learn.microsoft.com/zh-cn/windows-server/get-started/kms-client-activation-keys?tabs=server2025%2Cwindows1110ltsc%2Cversion1803%2Cwindows81#generic-volume-license-keys-gvlk
|
||||
Reference in New Issue
Block a user