GlueGen v2.6.0-rc-20250712
GlueGen, Native Binding Generator for Java™ (public API).
WeakIdentityHashMap.java
Go to the documentation of this file.
1/**
2 * Copyright 2019 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 *
28 * Original source code of this class taken from Apache Avro
29 * <https://github.com/apache/avro/blob/master/lang/java/avro/src/main/java/org/apache/avro/util/WeakIdentityHashMap.java>
30 * commit 70260919426f89825ca148f5ee815f3b2cf4764d.
31 * Up until commit 70260919426f89825ca148f5ee815f3b2cf4764d,
32 * this code has been licensed as described below:
33 *
34 * Licensed to the Apache Software Foundation (ASF) under one
35 * or more contributor license agreements. See the NOTICE file
36 * distributed with this work for additional information
37 * regarding copyright ownership. The ASF licenses this file
38 * to you under the Apache License, Version 2.0 (the
39 * "License"); you may not use this file except in compliance
40 * with the License. You may obtain a copy of the License at
41 *
42 * https://www.apache.org/licenses/LICENSE-2.0
43 *
44 * Unless required by applicable law or agreed to in writing,
45 * software distributed under the License is distributed on an
46 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
47 * KIND, either express or implied. See the License for the
48 * specific language governing permissions and limitations
49 * under the License.
50 */
51
52package com.jogamp.common.util;
53
54import java.lang.ref.ReferenceQueue;
55import java.lang.ref.WeakReference;
56import java.util.Collection;
57import java.util.Collections;
58import java.util.HashMap;
59import java.util.HashSet;
60import java.util.Map;
61import java.util.Set;
62
63/**
64 * Implements a combination of WeakHashMap and IdentityHashMap. Useful for
65 * caches that need to key off of a == comparison instead of a .equals.
66 *
67 * <b> This class is not a general-purpose Map implementation! While this class
68 * implements the Map interface, it intentionally violates Map's general
69 * contract, which mandates the use of the equals method when comparing objects.
70 * This class is designed for use only in the rare cases wherein
71 * reference-equality semantics are required.
72 *
73 * Note that this implementation is not synchronized. </b>
74 */
75public class WeakIdentityHashMap<K, V> implements Map<K, V> {
76 private final ReferenceQueue<K> queue = new ReferenceQueue<>();
77 private final Map<IdentityWeakReference<K>, V> backingStore;
78
79 /**
80 * See {@link HashMap#HashMap()}
81 */
83 backingStore = new HashMap<>();
84 }
85
86 /**
87 * See {@link HashMap#HashMap(int, float)}
88 * <p>
89 * Usable slots before resize are {@code capacity * loadFactor}.
90 * </p>
91 * <p>
92 * Capacity for n-slots w/o resize would be {@code (float)n/loadFactor + 1.0f}, see {@link #capacityForRequiredSize(int, float[])}.
93 * </p>
94 * @param initialCapacity default value would be 16, i.e. 12 slots @ 0.75f loadFactor before resize
95 * @param loadFactor default value would be 0.75f
96 * @see #capacityForRequiredSize(int, float[])
97 * @see #createWithRequiredSize(int, float)
98 */
99 public WeakIdentityHashMap(final int initialCapacity, final float loadFactor) {
100 backingStore = new HashMap<>(initialCapacity, loadFactor);
101 }
102
103 /**
104 * Static creation method using {@link #capacityForRequiredSize(int, float[])}
105 * to instantiate a new {@link WeakIdentityHashMap} via {@link #WeakIdentityHashMap(int, float)}.
106 *
107 * @param requiredSize the user desired n-slots before resize
108 * @param loadFactor given loadFactor, which might be increased a little to avoid next PowerOf2 bloat
109 * @return the new {@link WeakIdentityHashMap} instance
110 */
111 @SuppressWarnings("rawtypes")
112 public static WeakIdentityHashMap<?, ?> createWithRequiredSize(final int requiredSize, final float loadFactor) {
113 final float[] lf = { loadFactor };
114 final int icap = capacityForRequiredSize(requiredSize, lf);
115 return new WeakIdentityHashMap(icap, lf[0]);
116 }
117
118 /**
119 * Returns the [initial] capacity using the given {@code loadFactor}
120 * and {@code requiredSize}.
121 * <p>
122 * General calculation is {@code (float)requiredSize/loadFactor + 1.0f}, using {@code loadFactor := 0.75f}.
123 * </p>
124 * <p>
125 * In case above computed capacity is {@link Bitfield.Util#isPowerOf2(int)},
126 * the given {@code loadFactor} will be increased to avoid next PowerOf2 table size initialization.
127 * </p>
128 * @param requiredSize the user desired n-slots before resize
129 * @param loadFactor given loadFactor, which might be increased a little to avoid next PowerOf2 bloat
130 * @return the [initial] capacity to be used for {@link #WeakIdentityHashMap(int, float)}
131 */
132 public static int capacityForRequiredSize(final int requiredSize, final float[] loadFactor) {
133 if( requiredSize >= Bitfield.Util.MAX_POWER_OF_2 ) {
134 return Integer.MAX_VALUE;
135 }
136 float lf = loadFactor[0];
137 int c0 = (int)( requiredSize/lf + 1.0f );
138 if( !Bitfield.Util.isPowerOf2(c0) || 0.86f <= lf ) {
139 return c0;
140 }
141 do {
142 lf += 0.01f;
143 c0 = (int)( requiredSize/lf + 1.0f );
144 } while( Bitfield.Util.isPowerOf2(c0) && 0.86f > lf );
145
146 loadFactor[0] = lf;
147 return c0;
148 }
149
150 @Override
151 public void clear() {
152 backingStore.clear();
153 reap();
154 }
155
156 @SuppressWarnings("unchecked")
157 @Override
158 public boolean containsKey(final Object key) {
159 reap();
160 return backingStore.containsKey(new IdentityWeakReference<K>((K) key, queue));
161 }
162
163 @Override
164 public boolean containsValue(final Object value) {
165 reap();
166 return backingStore.containsValue(value);
167 }
168
169 @Override
170 public Set<Map.Entry<K, V>> entrySet() {
171 reap();
172 final Set<Map.Entry<K, V>> ret = new HashSet<>();
173 for (final Map.Entry<IdentityWeakReference<K>, V> ref : backingStore.entrySet()) {
174 final K key = ref.getKey().get();
175 if( null != key ) {
176 final V value = ref.getValue();
177 final Map.Entry<K, V> entry = new Map.Entry<K, V>() {
178 @Override
179 public K getKey() {
180 return key;
181 }
182
183 @Override
184 public V getValue() {
185 return value;
186 }
187
188 @Override
189 public V setValue(final V value) {
190 throw new UnsupportedOperationException();
191 }
192 };
193 ret.add(entry);
194 }
195 }
196 return Collections.unmodifiableSet(ret);
197 }
198
199 @Override
200 public Set<K> keySet() {
201 reap();
202 final Set<K> ret = new HashSet<>();
203 for (final IdentityWeakReference<K> ref : backingStore.keySet()) {
204 final K key = ref.get();
205 if( null != key ) {
206 ret.add(key);
207 }
208 }
209 return Collections.unmodifiableSet(ret);
210 }
211
212 @Override
213 public boolean equals(final Object o) {
214 if (!(o instanceof WeakIdentityHashMap)) {
215 return false;
216 }
217 return backingStore.equals(((WeakIdentityHashMap<?, ?>) o).backingStore);
218 }
219
220 @SuppressWarnings("unchecked")
221 @Override
222 public V get(final Object key) {
223 reap();
224 return backingStore.get(new IdentityWeakReference<K>((K) key, queue));
225 }
226
227 @Override
228 public V put(final K key, final V value) {
229 reap();
230 return backingStore.put(new IdentityWeakReference<K>(key, queue), value);
231 }
232
233 @Override
234 public int hashCode() {
235 reap();
236 return backingStore.hashCode();
237 }
238
239 @Override
240 public boolean isEmpty() {
241 reap();
242 return backingStore.isEmpty();
243 }
244
245 @Override
246 public void putAll(final Map<? extends K, ? extends V> t) {
247 final int n = t.size();
248 if ( 0 < n ) {
249 final float[] lf = { 0.75f };
250 final int icap = capacityForRequiredSize(n, lf);
251 final Map<IdentityWeakReference<K>, V> t2 = new HashMap<>(icap, lf[0]);
252 for (final Map.Entry<? extends K, ? extends V> e : t.entrySet()) {
253 t2.put(new IdentityWeakReference<K>(e.getKey(), queue), e.getValue());
254 }
255 backingStore.putAll(t2);
256 reap();
257 }
258 }
259
260 @SuppressWarnings("unchecked")
261 @Override
262 public V remove(final Object key) {
263 reap();
264 return backingStore.remove(new IdentityWeakReference<K>((K) key, queue));
265 }
266
267 @Override
268 public int size() {
269 reap();
270 return backingStore.size();
271 }
272
273 @Override
274 public Collection<V> values() {
275 reap();
276 return backingStore.values();
277 }
278
279 private synchronized void reap() {
280 Object zombie = queue.poll();
281
282 while (zombie != null) {
283 @SuppressWarnings("unchecked")
284 final IdentityWeakReference<K> victim = (IdentityWeakReference<K>) zombie;
285 backingStore.remove(victim);
286 zombie = queue.poll();
287 }
288 }
289
290 private static class IdentityWeakReference<K> extends WeakReference<K> {
291 final int hash;
292
293 IdentityWeakReference(final K obj, final ReferenceQueue<K> q) {
294 super(obj, q);
295 hash = System.identityHashCode(obj);
296 }
297
298 @Override
299 public int hashCode() {
300 return hash;
301 }
302
303 @Override
304 public boolean equals(final Object o) {
305 if (this == o) {
306 return true;
307 }
308 if (!(o instanceof IdentityWeakReference)) {
309 return false;
310 }
311 @SuppressWarnings("unchecked")
312 final IdentityWeakReference<K> ref = (IdentityWeakReference<K>) o;
313 return this.get() == ref.get();
314 }
315 }
316}
Bit operation utilities (static).
Definition: Bitfield.java:43
static final boolean isPowerOf2(final int n)
Returns true if the given integer is a power of 2.
Definition: Bitfield.java:108
static final int MAX_POWER_OF_2
Maximum 32bit integer value being of isPowerOf2(int).
Definition: Bitfield.java:50
Implements a combination of WeakHashMap and IdentityHashMap.
WeakIdentityHashMap(final int initialCapacity, final float loadFactor)
See HashMap#HashMap(int, float).
static WeakIdentityHashMap<?, ?> createWithRequiredSize(final int requiredSize, final float loadFactor)
Static creation method using capacityForRequiredSize(int, float[]) to instantiate a new WeakIdentityH...
static int capacityForRequiredSize(final int requiredSize, final float[] loadFactor)
Returns the [initial] capacity using the given loadFactor and requiredSize.
void putAll(final Map<? extends K, ? extends V > t)
Simple bitfield interface for efficient bit storage access in O(1).
Definition: Bitfield.java:36