• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package jdk.internal.util;
27 
28 import java.lang.ref.Reference;
29 import java.lang.ref.ReferenceQueue;
30 import java.lang.ref.SoftReference;
31 import java.lang.ref.WeakReference;
32 import java.util.*;
33 import java.util.concurrent.ConcurrentHashMap;
34 import java.util.function.Supplier;
35 import java.util.function.UnaryOperator;
36 
37 /**
38  * This class provides management of {@link Set set} where it is desirable to
39  * remove elements automatically when the element is garbage collected. This is
40  * accomplished by using a backing map where the keys and values are either a
41  * {@link WeakReference} or a {@link SoftReference}.
42  * <p>
43  * To create a {@link ReferencedKeySet} the user must provide a {@link Supplier}
44  * of the backing map and whether {@link WeakReference} or
45  * {@link SoftReference} is to be used.
46  * {@snippet :
47  * Set<Long> set;
48  *
49  * set = ReferencedKeySet.create(false, HashMap::new);
50  * set.add(30_000_000L);
51  * set.add(30_000_001L);
52  * set.add(30_000_002L);
53  * set.add(30_000_003L);
54  * set.add(30_000_004L);
55  *
56  * set = ReferencedKeySet.create(true, ConcurrentHashMap::new);
57  * set.add(40_000_000L);
58  * set.add(40_000_001L);
59  * set.add(40_000_002L);
60  * set.add(40_000_003L);
61  * set.add(40_000_004L);
62  * }
63  *
64  * @implNote Care must be given that the backing map does replacement by
65  * replacing the value in the map entry instead of deleting the old entry and
66  * adding a new entry, otherwise replaced entries may end up with a strongly
67  * referenced key. {@link HashMap} and {@link ConcurrentHashMap} are known
68  * to be safe.
69  *
70  * @param <T> the type of elements maintained by this set
71  */
72 public final class ReferencedKeySet<T> extends AbstractSet<T> {
73     /**
74      * Backing {@link ReferencedKeySet} map.
75      */
76     final ReferencedKeyMap<T, ReferenceKey<T>> map;
77 
78     /**
79      * Private constructor.
80      *
81      * @param map     backing map
82      */
ReferencedKeySet(ReferencedKeyMap<T, ReferenceKey<T>> map)83     private ReferencedKeySet(ReferencedKeyMap<T, ReferenceKey<T>> map) {
84         this.map = map;
85     }
86 
87     /**
88      * Create a new {@link ReferencedKeySet} elements.
89      *
90      * @param isSoft          true if {@link SoftReference} elements are to
91      *                        be used, {@link WeakReference} otherwise.
92      * @param supplier        {@link Supplier} of the backing map
93      *
94      * @return a new set with {@link Reference} elements
95      *
96      * @param <E> the type of elements maintained by this set
97      */
98     // Android-changed: made private so no one tries to create a map with a non-native queue.
99     // public static <E> ReferencedKeySet<E>
100     private static <E> ReferencedKeySet<E>
create(boolean isSoft, Supplier<Map<ReferenceKey<E>, ReferenceKey<E>>> supplier)101     create(boolean isSoft, Supplier<Map<ReferenceKey<E>, ReferenceKey<E>>> supplier) {
102         return create(isSoft, false, supplier);
103     }
104 
105     /**
106      * Create a new {@link ReferencedKeySet} elements.
107      *
108      * @param isSoft          true if {@link SoftReference} elements are to
109      *                        be used, {@link WeakReference} otherwise.
110      * @param useNativeQueue  true if uses NativeReferenceQueue
111      *                        otherwise use {@link ReferenceQueue}.
112      * @param supplier        {@link Supplier} of the backing map
113      *
114      * @return a new set with {@link Reference} elements
115      *
116      * @param <E> the type of elements maintained by this set
117      */
118     // Android-changed: made private so no one tries to create a map with a non-native queue.
119     // public static <E> ReferencedKeySet<E>
120     private static <E> ReferencedKeySet<E>
create(boolean isSoft, boolean useNativeQueue, Supplier<Map<ReferenceKey<E>, ReferenceKey<E>>> supplier)121     create(boolean isSoft, boolean useNativeQueue, Supplier<Map<ReferenceKey<E>, ReferenceKey<E>>> supplier) {
122          return new ReferencedKeySet<>(ReferencedKeyMap.create(isSoft, useNativeQueue, supplier));
123     }
124 
125     // Android-added: add a separate method with explicit name instead of passing boolean arg.
126     public static <E> ReferencedKeySet<E>
createWithNativeQueue(boolean isSoft, Supplier<Map<ReferenceKey<E>, ReferenceKey<E>>> supplier)127     createWithNativeQueue(boolean isSoft, Supplier<Map<ReferenceKey<E>, ReferenceKey<E>>> supplier) {
128         return new ReferencedKeySet<>(ReferencedKeyMap.createWithNativeQueue(isSoft, supplier));
129     }
130 
131     /**
132      * Removes enqueued weak references from set.
133      */
removeStaleReferences()134     public void removeStaleReferences() {
135         map.removeStaleReferences();
136     }
137 
138     @Override
iterator()139     public Iterator<T> iterator() {
140         return map.keySet().iterator();
141     }
142 
143     @Override
size()144     public int size() {
145         return map.size();
146     }
147 
148     @Override
isEmpty()149     public boolean isEmpty() {
150         return map.isEmpty();
151     }
152 
153     @Override
154     @SuppressWarnings("unchecked")
contains(Object o)155     public boolean contains(Object o) {
156         return map.containsKey((T)o);
157     }
158 
159     @Override
add(T e)160     public boolean add(T e) {
161         return ReferencedKeyMap.internAddKey(map, e);
162     }
163 
164     @Override
remove(Object o)165     public boolean remove(Object o) {
166         return map.remove(o) != null;
167     }
168 
169     @Override
clear()170     public void clear() {
171         map.clear();
172     }
173 
174     /**
175      * Gets an existing element from the set, returning null if not present or
176      * the old element if previously added.
177      *
178      * @param e  element to get
179      *
180      * @return the old element if present, otherwise, null
181      */
get(T e)182     public T get(T e) {
183         ReferenceKey<T> key = map.get(e);
184 
185         return key == null ? null : key.get();
186     }
187 
188     /**
189      * Intern an element to the set, returning the element if newly added or the
190      * old element if previously added.
191      *
192      * @param e  element to add
193      *
194      * @return the old element if present, otherwise, the new element
195      */
intern(T e)196     public T intern(T e) {
197         return ReferencedKeyMap.intern(map, e);
198     }
199 
200     /**
201      * Intern an element to the set, returning the element if newly added or the
202      * old element if previously added.
203      *
204      * @param e         element to add
205      * @param interner  operation to apply to key before adding to set
206      *
207      * @return the old element if present, otherwise, the new element
208      *
209      * @implNote This version of intern should not be called during phase1
210      * using a lambda. Use an UnaryOperator instance instead.
211      */
intern(T e, UnaryOperator<T> interner)212     public T intern(T e, UnaryOperator<T> interner) {
213         return ReferencedKeyMap.intern(map, e, interner);
214     }
215 }
216