home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1995 November / PCWK1195.iso / inne / win95 / sieciowe / hotja32.lzh / hotjava / classsrc / java / util / cache.java < prev    next >
Text File  |  1995-08-11  |  9KB  |  320 lines

  1. /*
  2.  * @(#)Cache.java    1.2 95/04/04
  3.  * 
  4.  * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * Permission to use, copy, modify, and distribute this software and its
  7.  * documentation for NON-COMMERCIAL purposes and without fee is hereby
  8.  * granted provided that this copyright notice appears in all copies. Please
  9.  * refer to the file "copyright.html" for further important copyright and
  10.  * licensing information.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
  15.  * OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
  16.  * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR
  17.  * ITS DERIVATIVES.
  18.  */
  19.  
  20. package java.util;
  21.  
  22. /**
  23.  * Cache collision list.
  24.  */
  25. class CacheEntry extends Ref {
  26.     int hash;
  27.     Object key;
  28.     CacheEntry next;
  29.     public Object reconstitute() {
  30.     return null;
  31.     }
  32. }
  33.  
  34. /**
  35.  * Cache class. Maps keys to values. Any object can be used as
  36.  * a key and/or value.  This is very similar to the Hashtable
  37.  * class, except that after putting an object into the Cache,
  38.  * it is not guranteed that a subsequent get will return it.
  39.  * The cache will automatically remove entries if memory is
  40.  * getting tight and the entry isn't referenced from outside
  41.  * the cache.<p>
  42.  *
  43.  * To sucessfully store and retrieve objects from a hash table the
  44.  * object used as the key must implement the hashCode() and equals()
  45.  * methods.<p>
  46.  *
  47.  * This example creates a cache of numbers. It uses the names of
  48.  * the numbers as keys:
  49.  * <pre>
  50.  *    Cache numbers = new Cache();
  51.  *    numbers.put("one", new Integer(1));
  52.  *    numbers.put("two", new Integer(1));
  53.  *    numbers.put("three", new Integer(1));
  54.  * </pre>
  55.  * To retrieve a number use:
  56.  * <pre>
  57.  *    Integer n = (Integer)numbers.get("two");
  58.  *    if (n != null) {
  59.  *        System.out.println("two = " + n);
  60.  *    }
  61.  * </pre>
  62.  *
  63.  * @see java.lang.Object#hashCode
  64.  * @see java.lang.Object#equals
  65.  * @see java.lang.Ref
  66.  * @version     1.20, 14 Mar 1995
  67.  * @author    Arthur van Hoff
  68.  */
  69. public
  70. class Cache {
  71.     /**
  72.      * The hash table data.
  73.      */
  74.     private CacheEntry table[];
  75.  
  76.     /**
  77.      * The total number of entries in the hash table.
  78.      */
  79.     private int count;
  80.  
  81.     /**
  82.      * Rehashes the table when count exceeds this threshold.
  83.      */
  84.     private int threshold;
  85.  
  86.     /**
  87.      * The load factor for the hashTable.
  88.      */
  89.     private float loadFactor;
  90.  
  91.     /**
  92.      * Construct a new, empty cache with the specified initial capacity
  93.      * and the specified load factor.
  94.      * @param initialCapacity the initial number of buckets
  95.      * @param loadFactor a number between 0.0 and 1.0, it defines
  96.      *        the threshold for rehashing the cache into
  97.      *        a bigger one.
  98.      */
  99.     public Cache (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 capacity.
  110.      * @param initialCapacity the initial number of buckets
  111.      */
  112.     public Cache (int initialCapacity) {
  113.     this(initialCapacity, 0.75);
  114.     }
  115.  
  116.     /**
  117.      * Constructs a new, empty cache. A default capacity and load factor
  118.      * is used. Note that the cache will automatically grow when it gets
  119.      * full.
  120.      */
  121.     public Cache () {
  122.     this(101, 0.75);
  123.     }
  124.  
  125.     /**
  126.      * Returns the cache's size (the number of elements it contains).
  127.      */
  128.     public int size() {
  129.     return count;
  130.     }
  131.  
  132.     /**
  133.      * Returns true if the cache contains no elements.
  134.      */
  135.     public boolean isEmpty() {
  136.     return count == 0;
  137.     }
  138.  
  139.     /**
  140.      * Returns an enumeration of the cache's keys.
  141.      * @see Cache#elements
  142.      * @see Enumeration
  143.      */
  144.     public synchronized Enumeration keys() {
  145.     return new CacheEnumerator(table, true);
  146.     }
  147.  
  148.     /**
  149.      * Returns an enumeration of the elements. Use the Enumeration methods on
  150.      * the returned object to fetch the elements sequentially.
  151.      * @see Cache#keys
  152.      * @see Enumeration
  153.      */
  154.     public synchronized Enumeration elements() {
  155.     return new CacheEnumerator(table, false);
  156.     }
  157.  
  158.     /**
  159.      * Gets the object associated with a key in the cache.
  160.      * @returns the element for the key or null if the key
  161.      *         is not defined in the hash table.
  162.      * @see Cache#put
  163.      */
  164.     public synchronized Object get(Object key) {
  165.     CacheEntry tab[] = table;
  166.     int hash = key.hashCode();
  167.     int index = (hash & 0x7FFFFFFF) % tab.length;
  168.     for (CacheEntry e = tab[index]; e != null; e = e.next) {
  169.         if ((e.hash == hash) && e.key.equals(key)) {
  170.         return e.check();
  171.         }
  172.     }
  173.     return null;
  174.     }
  175.  
  176.     /**
  177.      * Rehashes the content of the table into a bigger table.
  178.      * This is method called automatically when the cache's
  179.      * size exeeds a threshold.
  180.      */
  181.     protected void rehash() {
  182.     int oldCapacity = table.length;
  183.     CacheEntry oldTable[] = table;
  184.  
  185.     int newCapacity = oldCapacity * 2 + 1;
  186.     CacheEntry newTable[] = new CacheEntry[newCapacity];
  187.  
  188.     threshold = (int) (newCapacity * loadFactor);
  189.     table = newTable;
  190.  
  191.     // System.out.println("rehash old=" + oldCapacity + ", new=" +
  192.     // newCapacity + ", thresh=" + threshold + ", count=" + count);
  193.  
  194.     for (int i = oldCapacity; i-- > 0;) {
  195.         for (CacheEntry old = oldTable[i]; old != null;) {
  196.         CacheEntry e = old;
  197.         old = old.next;
  198.         if (e.check() != null) {
  199.             int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  200.             e.next = newTable[index];
  201.             newTable[index] = e;
  202.         } else
  203.             count--;    /* remove entries that have disappeared */
  204.         }
  205.     }
  206.     }
  207.  
  208.     /**
  209.      * Puts the specified element into the cache, using the specified
  210.      * key.  The element may be retrieved by doing a get() with the same key.
  211.      * The key can't be null and the element can't be null.
  212.      * @see Cache#get
  213.      * @return the old value of the key, or null if it didn't have one
  214.      */
  215.     public synchronized Object put(Object key, Object value) {
  216.     // Make sure the value is not null
  217.     if (value == null) {
  218.         throw new NullPointerException();
  219.     }
  220.     // Makes sure the key is not already in the cache.
  221.     CacheEntry tab[] = table;
  222.     int hash = key.hashCode();
  223.     int index = (hash & 0x7FFFFFFF) % tab.length;
  224.     CacheEntry ne = null;
  225.     for (CacheEntry e = tab[index]; e != null; e = e.next) {
  226.         if ((e.hash == hash) && e.key.equals(key)) {
  227.         Object old = e.check();
  228.         e.setThing(value);
  229.         return old;
  230.         } else if (e.check() == null)
  231.         ne = e;        /* reuse old flushed value */
  232.     }
  233.  
  234.     if (count >= threshold) {
  235.         // Rehash the table if the threshold is exceeded
  236.         rehash();
  237.         return put(key, value);
  238.     }
  239.     // Creates the new entry.
  240.     if (ne == null) {
  241.         ne = new CacheEntry ();
  242.         ne.next = tab[index];
  243.         tab[index] = ne;
  244.         count++;
  245.     }
  246.     ne.hash = hash;
  247.     ne.key = key;
  248.     ne.setThing(value);
  249.     return null;
  250.     }
  251.  
  252.     /**
  253.      * Removes the element corresponding to the key. Does nothing if the
  254.      * key isn't present.
  255.      * @param key the key that needs to be removed
  256.      * @return the value of key, or null if the key was not found
  257.      */
  258.     public synchronized Object remove(Object key) {
  259.     CacheEntry tab[] = table;
  260.     int hash = key.hashCode();
  261.     int index = (hash & 0x7FFFFFFF) % tab.length;
  262.     for (CacheEntry e = tab[index], prev = null; e != null; prev = e, e = e.next) {
  263.         if ((e.hash == hash) && e.key.equals(key)) {
  264.         if (prev != null) {
  265.             prev.next = e.next;
  266.         } else {
  267.             tab[index] = e.next;
  268.         }
  269.         count--;
  270.         return e.check();
  271.         }
  272.     }
  273.     return null;
  274.     }
  275. }
  276.  
  277. /**
  278.  * A cache enumerator class.This class should remain opague
  279.  * to the client. It will use the Enumeration interface.
  280.  */
  281. class CacheEnumerator implements Enumeration {
  282.     boolean keys;
  283.     int index;
  284.     CacheEntry table[];
  285.     CacheEntry entry;
  286.  
  287.     CacheEnumerator (CacheEntry table[], boolean keys) {
  288.     this.table = table;
  289.     this.keys = keys;
  290.     this.index = table.length;
  291.     }
  292.  
  293.     public boolean hasMoreElements() {
  294.     while (index >= 0) {
  295.         while (entry != null)
  296.         if (entry.check() != null)
  297.             return true;
  298.         else
  299.             entry = entry.next;
  300.         while (--index >= 0 && (entry = table[index]) != null) ;
  301.     }
  302.     return false;
  303.     }
  304.  
  305.     public Object nextElement() {
  306.     while (index >= 0) {
  307.         if (entry == null)
  308.         while (--index >= 0 && (entry = table[index]) == null) ;
  309.         if (entry != null) {
  310.         CacheEntry e = entry;
  311.         entry = e.next;
  312.         if (e.check() != null)
  313.             return keys ? e.key : e.check();
  314.         }
  315.     }
  316.     throw new NoSuchElementException("CacheEnumerator");
  317.     }
  318.  
  319. }
  320.