forked from kangjianwei/LearningJDK
-
Notifications
You must be signed in to change notification settings - Fork 0
/
WeakPairMap.java
374 lines (338 loc) · 14.7 KB
/
WeakPairMap.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
/**
* A WeakHashMap-like data structure that uses a pair of weakly-referenced keys
* with identity equality semantics to associate a strongly-referenced value.
* Unlike WeakHashMap, this data structure is thread-safe.
*
* @param <K1> the type of 1st key in key pair
* @param <K2> the type of 2nd key in key pair
* @param <V> the type of value
*
* @author Peter Levart
*/
// 双键Map: 该Map中每个实体拥有两个key,一个value,这两个key均被弱引用追踪
final class WeakPairMap<K1, K2, V> {
// 双键Map的实现:将两个key打包为一个Pair
private final ConcurrentHashMap<Pair<K1, K2>, V> map = new ConcurrentHashMap<>();
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
/**
* Tests if the specified pair of keys are associated with a value
* in the WeakPairMap.
*
* @param k1 the 1st of the pair of keys
* @param k2 the 2nd of the pair of keys
*
* @return true if and only if the specified key pair is in this WeakPairMap,
* as determined by the identity comparison; false otherwise
*
* @throws NullPointerException if any of the specified keys is null
*/
// 判断当前双键Map中是否包含指定的"键对"
public boolean containsKeyPair(K1 k1, K2 k2) {
expungeStaleAssociations();
return map.containsKey(Pair.lookup(k1, k2));
}
/**
* Returns the value to which the specified pair of keys is mapped, or null
* if this WeakPairMap contains no mapping for the key pair.
* <p>More formally, if this WeakPairMap contains a mapping from a key pair
* {@code (_k1, _k2)} to a value {@code v} such that
* {@code k1 == _k1 && k2 == _k2}, then this method returns {@code v};
* otherwise it returns {@code null}.
* (There can be at most one such mapping.)
*
* @param k1 the 1st of the pair of keys for which the mapped value is to
* be returned
* @param k2 the 2nd of the pair of keys for which the mapped value is to
* be returned
*
* @return the value to which the specified key pair is mapped, or null if
* this map contains no mapping for the key pair
*
* @throws NullPointerException if any of the specified keys is null
*/
// 返回当前双键Map中指定"键对"对应的value
public V get(K1 k1, K2 k2) {
expungeStaleAssociations();
return map.get(Pair.lookup(k1, k2));
}
/**
* Maps the specified key pair to the specified value in this WeakPairMap.
* Neither the keys nor the value can be null.
* <p>The value can be retrieved by calling the {@link #get} method
* with the same keys (compared by identity).
*
* @param k1 the 1st of the pair of keys with which the specified value is to
* be associated
* @param k2 the 2nd of the pair of keys with which the specified value is to
* be associated
* @param v value to be associated with the specified key pair
*
* @return the previous value associated with key pair, or {@code null} if
* there was no mapping for key pair
*
* @throws NullPointerException if any of the specified keys or value is null
*/
// 向双键Map中存入一个实体:两个key跟一个value,并返回旧值,允许覆盖
public V put(K1 k1, K2 k2, V v) {
expungeStaleAssociations();
return map.put(Pair.weak(k1, k2, queue), v);
}
/**
* If the specified key pair is not already associated with a value,
* associates it with the given value and returns {@code null}, else does
* nothing and returns the currently associated value.
*
* @param k1 the 1st of the pair of keys with which the specified value is to
* be associated
* @param k2 the 2nd of the pair of keys with which the specified value is to
* be associated
* @param v value to be associated with the specified key pair
*
* @return the previous value associated with key pair, or {@code null} if
* there was no mapping for key pair
*
* @throws NullPointerException if any of the specified keys or value is null
*/
// 向双键Map中存入一个实体:两个key跟一个value,并返回旧值,不允许覆盖
public V putIfAbsent(K1 k1, K2 k2, V v) {
expungeStaleAssociations();
return map.putIfAbsent(Pair.weak(k1, k2, queue), v);
}
/**
* If the specified key pair is not already associated with a value,
* attempts to compute its value using the given mapping function
* and enters it into this WeakPairMap unless {@code null}. The entire
* method invocation is performed atomically, so the function is
* applied at most once per key pair. Some attempted update operations
* on this WeakPairMap by other threads may be blocked while computation
* is in progress, so the computation should be short and simple,
* and must not attempt to update any other mappings of this WeakPairMap.
*
* @param k1 the 1st of the pair of keys with which the
* computed value is to be associated
* @param k2 the 2nd of the pair of keys with which the
* computed value is to be associated
* @param mappingFunction the function to compute a value
*
* @return the current (existing or computed) value associated with
* the specified key pair, or null if the computed value is null
*
* @throws NullPointerException if any of the specified keys or
* mappingFunction is null
* @throws IllegalStateException if the computation detectably
* attempts a recursive update to this map
* that would otherwise never complete
* @throws RuntimeException or Error if the mappingFunction does so, in
* which case the mapping is left unestablished
*/
// 插入/替换操作:不存在同位元素时,使用"键对"创造的新value来更新旧value;如果同位元素存在,则直接返回旧值
public V computeIfAbsent(K1 k1, K2 k2, BiFunction<? super K1, ? super K2, ? extends V> mappingFunction) {
expungeStaleAssociations();
try {
return map.computeIfAbsent(Pair.weak(k1, k2, queue), pair -> mappingFunction.apply(pair.first(), pair.second()));
} finally {
Reference.reachabilityFence(k1);
Reference.reachabilityFence(k2);
}
}
/**
* Returns a {@link Collection} view of the values contained in this
* WeakPairMap. The collection is backed by the WeakPairMap, so changes to
* the map are reflected in the collection, and vice-versa. The collection
* supports element removal, which removes the corresponding
* mapping from this map, via the {@code Iterator.remove},
* {@code Collection.remove}, {@code removeAll},
* {@code retainAll}, and {@code clear} operations. It does not
* support the {@code add} or {@code addAll} operations.
*
* @return the collection view
*/
// 返回值的集合
public Collection<V> values() {
expungeStaleAssociations();
return map.values();
}
/**
* Removes associations from this WeakPairMap for which at least one of the
* keys in key pair has been found weakly-reachable and corresponding
* WeakRefPeer(s) enqueued. Called as part of each public operation.
*/
// 从双键Map中移除那些key被回收的实体
private void expungeStaleAssociations() {
WeakRefPeer<?> peer;
while((peer = (WeakRefPeer<?>) queue.poll()) != null) {
map.remove(peer.weakPair());
}
}
/**
* Common interface of both {@link Weak} and {@link Lookup} key pairs.
*/
// 双键Map中的key
private interface Pair<K1, K2> {
/**
* @return The 1st of the pair of keys (may be null for {@link Weak}
* when it gets cleared)
*/
K1 first();
/**
* @return The 2nd of the pair of keys (may be null for {@link Weak}
* when it gets cleared)
*/
K2 second();
static <K1, K2> Pair<K1, K2> weak(K1 k1, K2 k2, ReferenceQueue<Object> queue) {
return new Weak<>(k1, k2, queue);
}
static <K1, K2> Pair<K1, K2> lookup(K1 k1, K2 k2) {
return new Lookup<>(k1, k2);
}
static int hashCode(Object first, Object second) {
// assert first != null && second != null;
return System.identityHashCode(first) ^ System.identityHashCode(second);
}
static boolean equals(Object first, Object second, Pair<?, ?> p) {
return first != null && second != null && first == p.first() && second == p.second();
}
/**
* A Pair where both keys are weakly-referenced.
* It is composed of two instances of {@link WeakRefPeer}s:
* <pre>{@code
*
* +-referent-> [K1] +-referent-> [K2]
* | |
* +----------------+ +----------------+
* | Pair.Weak <: |-----peer----->| (anonymous) <: |
* | WeakRefPeer, | | WeakRefPeer |
* | Pair |<--weakPair()--| |
* +----------------+ +----------------+
* | ^
* | |
* +-weakPair()-+
*
* }</pre>
* <p>
* Pair.Weak is used for CHM keys. Both peers are associated with the
* same {@link ReferenceQueue} so when either of their referents
* becomes weakly-reachable, the corresponding entries can be
* {@link #expungeStaleAssociations() expunged} from the map.
*/
// 弱引用键,除自身是WeakRefPeer外,还持有另一个WeakRefPeer
final class Weak<K1, K2> extends WeakRefPeer<K1> implements Pair<K1, K2> {
// link to <K2> peer
private final WeakRefPeer<K2> peer;
// saved hash so it can be retrieved after the reference is cleared
private final int hash;
Weak(K1 k1, K2 k2, ReferenceQueue<Object> queue) {
super(k1, queue);
hash = Pair.hashCode(k1, k2);
peer = new WeakRefPeer<>(k2, queue) {
// link back to <K1> peer
@Override
Weak<?, ?> weakPair() {
return Weak.this;
}
};
}
// 返回当前弱引用键
@Override
Weak<?, ?> weakPair() {
return this;
}
// 返回第一个key
@Override
public K1 first() {
return get();
}
// 返回第二个key
@Override
public K2 second() {
return peer.get();
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
return this == obj || (obj instanceof Pair && Pair.equals(first(), second(), (Pair<?, ?>) obj));
}
}
/**
* Optimized lookup Pair, used as lookup key in methods like
* {@link java.util.Map#get(Object)} or
* {@link java.util.Map#containsKey(Object)}) where
* there is a great chance its allocation is eliminated
* by escape analysis when such lookups are inlined by JIT.
* All its methods are purposely designed so that 'this' is never
* passed to any other method or used as identity.
*/
final class Lookup<K1, K2> implements Pair<K1, K2> {
private final K1 k1;
private final K2 k2;
Lookup(K1 k1, K2 k2) {
this.k1 = Objects.requireNonNull(k1);
this.k2 = Objects.requireNonNull(k2);
}
@Override
public K1 first() {
return k1;
}
@Override
public K2 second() {
return k2;
}
@Override
public int hashCode() {
return Pair.hashCode(k1, k2);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Pair && Pair.equals(k1, k2, (Pair<?, ?>) obj);
}
}
}
/**
* Common abstract supertype of a pair of WeakReference peers.
*/
// 包装key的弱引用
private static abstract class WeakRefPeer<K> extends WeakReference<K> {
WeakRefPeer(K k, ReferenceQueue<Object> queue) {
super(Objects.requireNonNull(k), queue);
}
/**
* @return the {@link Pair.Weak} side of the pair of peers.
*/
abstract Pair.Weak<?, ?> weakPair();
}
}