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