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