1 /* 2 * Copyright (c) 1995, 2014, 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 sun.misc; 27 28 import java.lang.ref.SoftReference; 29 import java.util.Dictionary; 30 import java.util.Enumeration; 31 import java.util.NoSuchElementException; 32 33 /** 34 * Caches the collision list. 35 */ 36 class CacheEntry { 37 int hash; 38 Object key; 39 CacheEntry next; 40 SoftReference<Object> value; 41 42 public CacheEntry() { 43 value = null; 44 } 45 46 public CacheEntry(Object o) { 47 value = new SoftReference<>(o); 48 } 49 50 public Object get() { 51 return value.get(); 52 } 53 54 public void setThing(Object thing) { 55 value = new SoftReference<>(thing); 56 } 57 } 58 59 /** 60 * The Cache class. Maps keys to values. Any object can be used as 61 * a key and/or value. This is very similar to the Hashtable 62 * class, except that after putting an object into the Cache, 63 * it is not guaranteed that a subsequent get will return it. 64 * The Cache will automatically remove entries if memory is 65 * getting tight and if the entry is not referenced from outside 66 * the Cache.<p> 67 * 68 * To sucessfully store and retrieve objects from a hash table the 69 * object used as the key must implement the hashCode() and equals() 70 * methods.<p> 71 * 72 * This example creates a Cache of numbers. It uses the names of 73 * the numbers as keys: 74 * <pre> 75 * Cache numbers = new Cache(); 76 * numbers.put("one", new Integer(1)); 77 * numbers.put("two", new Integer(1)); 78 * numbers.put("three", new Integer(1)); 79 * </pre> 80 * To retrieve a number use: 81 * <pre> 82 * Integer n = (Integer)numbers.get("two"); 83 * if (n != null) { 84 * System.out.println("two = " + n); 85 * } 86 * </pre> 87 * 88 * @see java.lang.Object#hashCode 89 * @see java.lang.Object#equals 90 * @see sun.misc.Ref 91 * @deprecated Consider {@link java.util.LinkedHashMap} for LRU caches. 92 */ 93 @Deprecated 94 public 95 class Cache extends Dictionary<Object, Object> { 96 /** 97 * The hash table data. 98 */ 99 private CacheEntry table[]; 100 101 /** 102 * The total number of entries in the hash table. 103 */ 104 private int count; 105 106 /** 107 * Rehashes the table when count exceeds this threshold. 108 */ 109 private int threshold; 110 111 /** 112 * The load factor for the hashtable. 113 */ 114 private float loadFactor; 115 116 private void init(int initialCapacity, float loadFactor) { 117 if ((initialCapacity <= 0) || (loadFactor <= 0.0)) { 118 throw new IllegalArgumentException(); 119 } 120 this.loadFactor = loadFactor; 121 table = new CacheEntry[initialCapacity]; 122 threshold = (int) (initialCapacity * loadFactor); 123 } 124 125 /** 126 * Constructs a new, empty Cache with the specified initial 127 * capacity and the specified load factor. 128 * @param initialCapacity the initial number of buckets 129 * @param loadFactor a number between 0.0 and 1.0, it defines 130 * the threshold for rehashing the Cache into 131 * a bigger one. 132 * @exception IllegalArgumentException If the initial capacity 133 * is less than or equal to zero. 134 * @exception IllegalArgumentException If the load factor is 135 * less than or equal to zero. 136 */ 137 public Cache (int initialCapacity, float loadFactor) { 138 init(initialCapacity, loadFactor); 139 } 140 141 /** 142 * Constructs a new, empty Cache with the specified initial 143 * capacity. 144 * @param initialCapacity the initial number of buckets 145 */ 146 public Cache (int initialCapacity) { 147 init(initialCapacity, 0.75f); 148 } 149 150 /** 151 * Constructs a new, empty Cache. A default capacity and load factor 152 * is used. Note that the Cache will automatically grow when it gets 153 * full. 154 */ 155 public Cache () { 156 try { 157 init(101, 0.75f); 158 } catch (IllegalArgumentException ex) { 159 // This should never happen 160 throw new Error("panic"); 161 } 162 } 163 164 /** 165 * Returns the number of elements contained within the Cache. 166 */ 167 public int size() { 168 return count; 169 } 170 171 /** 172 * Returns true if the Cache contains no elements. 173 */ 174 public boolean isEmpty() { 175 return count == 0; 176 } 177 178 /** 179 * Returns an enumeration of the Cache's keys. 180 * @see Cache#elements 181 * @see Enumeration 182 */ 183 public synchronized Enumeration<Object> keys() { 184 return new CacheEnumerator(table, true); 185 } 186 187 /** 188 * Returns an enumeration of the elements. Use the Enumeration methods 189 * on the returned object to fetch the elements sequentially. 190 * @see Cache#keys 191 * @see Enumeration 192 */ 193 public synchronized Enumeration<Object> elements() { 194 return new CacheEnumerator(table, false); 195 } 196 197 /** 198 * Gets the object associated with the specified key in the Cache. 199 * @param key the key in the hash table 200 * @returns the element for the key or null if the key 201 * is not defined in the hash table. 202 * @see Cache#put 203 */ 204 public synchronized Object get(Object key) { 205 CacheEntry tab[] = table; 206 int hash = key.hashCode(); 207 int index = (hash & 0x7FFFFFFF) % tab.length; 208 for (CacheEntry e = tab[index]; e != null; e = e.next) { 209 if ((e.hash == hash) && e.key.equals(key)) { 210 return e.get(); 211 } 212 } 213 return null; 214 } 215 216 /** 217 * Rehashes the contents of the table into a bigger table. 218 * This is method is called automatically when the Cache's 219 * size exceeds the threshold. 220 */ 221 protected void rehash() { 222 int oldCapacity = table.length; 223 CacheEntry oldTable[] = table; 224 225 int newCapacity = oldCapacity * 2 + 1; 226 CacheEntry newTable[] = new CacheEntry[newCapacity]; 227 228 threshold = (int) (newCapacity * loadFactor); 229 table = newTable; 230 231 // System.out.println("rehash old=" + oldCapacity + ", new=" + 232 // newCapacity + ", thresh=" + threshold + ", count=" + count); 233 234 for (int i = oldCapacity; i-- > 0;) { 235 for (CacheEntry old = oldTable[i]; old != null;) { 236 CacheEntry e = old; 237 old = old.next; 238 if (e.get() != null) { 239 int index = (e.hash & 0x7FFFFFFF) % newCapacity; 240 e.next = newTable[index]; 241 newTable[index] = e; 242 } else 243 count--; /* remove entries that have disappeared */ 244 } 245 } 246 } 247 248 /** 249 * Puts the specified element into the Cache, using the specified 250 * key. The element may be retrieved by doing a get() with the same 251 * key. The key and the element cannot be null. 252 * @param key the specified hashtable key 253 * @param value the specified element 254 * @return the old value of the key, or null if it did not have one. 255 * @exception NullPointerException If the value of the specified 256 * element is null. 257 * @see Cache#get 258 */ 259 public synchronized Object put(Object key, Object value) { 260 // Make sure the value is not null 261 if (value == null) { 262 throw new NullPointerException(); 263 } 264 // Makes sure the key is not already in the cache. 265 CacheEntry tab[] = table; 266 int hash = key.hashCode(); 267 int index = (hash & 0x7FFFFFFF) % tab.length; 268 CacheEntry ne = null; 269 for (CacheEntry e = tab[index]; e != null; e = e.next) { 270 if ((e.hash == hash) && e.key.equals(key)) { 271 Object old = e.get(); 272 e.setThing(value); 273 return old; 274 } else if (e.get() == null) 275 ne = e; /* reuse old flushed value */ 276 } 277 278 if (count >= threshold) { 279 // Rehash the table if the threshold is exceeded 280 rehash(); 281 return put(key, value); 282 } 283 // Creates the new entry. 284 if (ne == null) { 285 ne = new CacheEntry (); 286 ne.next = tab[index]; 287 tab[index] = ne; 288 count++; 289 } 290 ne.hash = hash; 291 ne.key = key; 292 ne.setThing(value); 293 return null; 294 } 295 296 /** 297 * Removes the element corresponding to the key. Does nothing if the 298 * key is not present. 299 * @param key the key that needs to be removed 300 * @return the value of key, or null if the key was not found. 301 */ 302 public synchronized Object remove(Object key) { 303 CacheEntry tab[] = table; 304 int hash = key.hashCode(); 305 int index = (hash & 0x7FFFFFFF) % tab.length; 306 for (CacheEntry e = tab[index], prev = null; e != null; prev = e, e = e.next) { 307 if ((e.hash == hash) && e.key.equals(key)) { 308 if (prev != null) { 309 prev.next = e.next; 310 } else { 311 tab[index] = e.next; 312 } 313 count--; 314 return e.get(); 315 } 316 } 317 return null; 318 } 319 } 320 321 /** 322 * A Cache enumerator class. This class should remain opaque 323 * to the client. It will use the Enumeration interface. 324 */ 325 class CacheEnumerator implements Enumeration<Object> { 326 boolean keys; 327 int index; 328 CacheEntry table[]; 329 CacheEntry entry; 330 331 CacheEnumerator (CacheEntry table[], boolean keys) { 332 this.table = table; 333 this.keys = keys; 334 this.index = table.length; 335 } 336 337 public boolean hasMoreElements() { 338 while (index >= 0) { 339 while (entry != null) 340 if (entry.get() != null) 341 return true; 342 else 343 entry = entry.next; 344 while (--index >= 0 && (entry = table[index]) == null) ; 345 } 346 return false; 347 } 348 349 public Object nextElement() { 350 while (index >= 0) { 351 if (entry == null) 352 while (--index >= 0 && (entry = table[index]) == null) ; 353 if (entry != null) { 354 CacheEntry e = entry; 355 entry = e.next; 356 if (e.get() != null) 357 return keys ? e.key : e.get(); 358 } 359 } 360 throw new NoSuchElementException("CacheEnumerator"); 361 } 362 363 }