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