home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / text / IntHashtable.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  9.3 KB  |  279 lines

  1. /*
  2.  * @(#)IntHashtable.java    1.2 98/02/25
  3.  *
  4.  * (C) Copyright Taligent, Inc. 1996,1997 - All Rights Reserved
  5.  * (C) Copyright IBM Corp. 1996, 1997 - All Rights Reserved
  6.  */
  7.  
  8. package java.text;
  9.  
  10. /** Simple internal class for doing hash mapping. Much, much faster than the
  11.  * standard Hashtable for integer to integer mappings,
  12.  * and doesn't require object creation.<br>
  13.  * If a key is not found, the defaultValue is returned.
  14.  * Note: the keys are limited to values above Integer.MIN_VALUE+1.<br>
  15.  */
  16. final class IntHashtable {
  17.  
  18.     public IntHashtable () {
  19.         initialize(3);
  20.     }
  21.  
  22.     public IntHashtable (int initialSize) {
  23.         initialize(leastGreaterPrimeIndex((int)(initialSize/highWaterFactor)));
  24.     }
  25.  
  26.     public int size() {
  27.         return count;
  28.     }
  29.  
  30.     public boolean isEmpty() {
  31.         return count == 0;
  32.     }
  33.  
  34.     public void put(int key, int value) {
  35.         if (count > highWaterMark) {
  36.             rehash();
  37.         }
  38.         int index = find(key);
  39.         if (keyList[index] <= MAX_UNUSED) {      // deleted or empty
  40.             keyList[index] = key;
  41.             ++count;
  42.         }
  43.         values[index] = value;                                  // reset value
  44.     }
  45.  
  46.     public int get(int key) {
  47.         return values[find(key)];
  48.     }
  49.  
  50.     public void remove(int key) {
  51.         int index = find(key);
  52.         if (keyList[index] > MAX_UNUSED) {       // neither deleted nor empty
  53.             keyList[index] = DELETED;                        // set to deleted
  54.             values[index] = defaultValue;                        // set to deleted
  55.             --count;
  56.             if (count < lowWaterMark) {
  57.                 rehash();
  58.             }
  59.         }
  60.     }
  61.  
  62.     public int getDefaultValue() {
  63.         return defaultValue;
  64.     }
  65.  
  66.     public void setDefaultValue(int newValue) {
  67.         defaultValue = newValue;
  68.         rehash();
  69.     }
  70.  
  71.     public boolean equals (Object that) {
  72.         if (that.getClass() != this.getClass()) return false;
  73.  
  74.         IntHashtable other = (IntHashtable) that;
  75.         if (other.size() != count || other.defaultValue != defaultValue) {
  76.                 return false;
  77.         }
  78.         for (int i = 0; i < keyList.length; ++i) {
  79.             int key = keyList[i];
  80.             if (key > MAX_UNUSED && other.get(key) != values[i])
  81.                 return false;
  82.         }
  83.         return true;
  84.     }
  85.  
  86.     public Object clone ()
  87.                     throws CloneNotSupportedException {
  88.         IntHashtable result = (IntHashtable) super.clone();
  89.         values = (int[]) values.clone();
  90.         keyList = (int[])keyList.clone();
  91.         return result;
  92.     }
  93.  
  94.     // =======================PRIVATES============================
  95.     private int defaultValue = 0;
  96.  
  97.     // the tables have to have prime-number lengths. Rather than compute
  98.     // primes, we just keep a table, with the current index we are using.
  99.     private int primeIndex;
  100.  
  101.     // highWaterFactor determines the maximum number of elements before
  102.     // a rehash. Can be tuned for different performance/storage characteristics.
  103.     private static final float highWaterFactor = 0.4F;
  104.     private int highWaterMark;
  105.  
  106.     // lowWaterFactor determines the minimum number of elements before
  107.     // a rehash. Can be tuned for different performance/storage characteristics.
  108.     private static final float lowWaterFactor = 0.0F;
  109.     private int lowWaterMark;
  110.  
  111.     private int count;
  112.  
  113.     // we use two arrays to minimize allocations
  114.     private int[] values;
  115.     private int[] keyList;
  116.  
  117.     private static final int EMPTY   = Integer.MIN_VALUE;
  118.     private static final int DELETED = EMPTY + 1;
  119.     private static final int MAX_UNUSED = DELETED;
  120.  
  121.     private void initialize (int primeIndex) {
  122.         if (primeIndex < 0) {
  123.             primeIndex = 0;
  124.         } else if (primeIndex >= PRIMES.length) {
  125.             System.out.println("TOO BIG");
  126.             primeIndex = PRIMES.length - 1;
  127.             // throw new java.util.IllegalArgumentError();
  128.         }
  129.         this.primeIndex = primeIndex;
  130.         int initialSize = PRIMES[primeIndex];
  131.         values = new int[initialSize];
  132.         keyList = new int[initialSize];
  133.         for (int i = 0; i < initialSize; ++i) {
  134.             keyList[i] = EMPTY;
  135.             values[i] = defaultValue;
  136.         }
  137.         count = 0;
  138.         lowWaterMark = (int)(initialSize * lowWaterFactor);
  139.         highWaterMark = (int)(initialSize * highWaterFactor);
  140.     }
  141.  
  142.     private void rehash() {
  143.         int[] oldValues = values;
  144.         int[] oldkeyList = keyList;
  145.         int newPrimeIndex = primeIndex;
  146.         if (count > highWaterMark) {
  147.             ++newPrimeIndex;
  148.         } else if (count < lowWaterMark) {
  149.             newPrimeIndex -= 2;
  150.         }
  151.         initialize(newPrimeIndex);
  152.         for (int i = oldValues.length - 1; i >= 0; --i) {
  153.             int key = oldkeyList[i];
  154.             if (key > MAX_UNUSED) {
  155.                     putInternal(key, oldValues[i]);
  156.             }
  157.         }
  158.     }
  159.  
  160.     public void putInternal (int key, int value) {
  161.         int index = find(key);
  162.         if (keyList[index] < MAX_UNUSED) {      // deleted or empty
  163.             keyList[index] = key;
  164.             ++count;
  165.         }
  166.         values[index] = value;                                  // reset value
  167.     }
  168.  
  169.     private int find (int key) {
  170.         if (key <= MAX_UNUSED)
  171.             throw new IllegalArgumentException("key can't be less than 0xFFFFFFFE");
  172.         int firstDeleted = -1;  // assume invalid index
  173.         int index = (key ^ 0x4000000) % keyList.length;
  174.         if (index < 0) index = -index; // positive only
  175.         int jump = 0; // lazy evaluate
  176.         while (true) {
  177.             int tableHash = keyList[index];
  178.             if (tableHash == key) {                    // quick check
  179.                 return index;
  180.             } else if (tableHash > MAX_UNUSED) {    // neither correct nor unused
  181.                 // ignore
  182.             } else if (tableHash == EMPTY) {        // empty, end o' the line
  183.                 if (firstDeleted >= 0) {
  184.                         index = firstDeleted;           // reset if had deleted slot
  185.                 }
  186.                 return index;
  187.             } else if (firstDeleted < 0) {  // remember first deleted
  188.                     firstDeleted = index;
  189.             }
  190.             // System.out.println("Collision at: " + index);
  191.             if (jump == 0) {                                                        // lazy compute jump
  192.                 jump = (key % (keyList.length - 1));
  193.                 if (jump < 0) jump = -jump;
  194.                 ++jump;
  195.             }
  196.             //*/
  197.             index = (index + jump) % keyList.length;
  198.             //System.out.print(" => " + index);
  199.         }
  200.     }
  201.  
  202.     private static int leastGreaterPrimeIndex(int source) {
  203.         int i;
  204.         for (i = 0; i < PRIMES.length; ++i) {
  205.             if (source < PRIMES[i]) {
  206.                 break;
  207.             }
  208.         }
  209.         return (i == 0) ? 0 : (i - 1);
  210.     }
  211.  
  212.     // This list is the result of buildList below. Can be tuned for different
  213.     // performance/storage characteristics.
  214.     private static final int[] PRIMES = {
  215.         17, 37, 67, 131, 257,
  216.         521, 1031, 2053, 4099, 8209, 16411, 32771, 65537,
  217.         131101, 262147, 524309, 1048583, 2097169, 4194319, 8388617, 16777259,
  218.         33554467, 67108879, 134217757, 268435459, 536870923, 1073741827, 2147483647
  219.  
  220.         // finer-grained table
  221.         /*11, 37, 71, 127, 179, 257, 359, 491, 661, 887, 1181, 1553,
  222.         2053, 2683, 3517, 4591, 6007, 7817, 10193, 13291, 17291,
  223.         22481, 29251, 38053, 49499, 64373, 83701, 108863, 141511,
  224.         184003, 239231, 310997, 404321, 525649, 683377, 888397,
  225.         1154947, 1501447, 1951949, 2537501, 3298807, 4288439,
  226.         5575001, 7247533, 9421793, 12248389, 15922903, 20699753,
  227.         26909713, 34982639, 45477503, 59120749, 76856959, 99914123,
  228.         129888349, 168854831, 219511301, 285364721, 370974151,
  229.         482266423, 626946367, 815030309, 1059539417, 1377401287,
  230.         1790621681, 2147483647
  231.         //*/
  232.     };
  233.  
  234.     /*
  235.     public static void buildList() {
  236.         String currentLine = "";
  237.         for (double target = 8; target < 0x7FFFFFFF; target = 2 * target) {
  238.                 int nextPrime = leastPrimeAsLargeAs((int)target);
  239.                 if (nextPrime <= 0) break;
  240.                 String addition = nextPrime + ", ";
  241.                 if (currentLine.length() + addition.length() > 60) {
  242.                         System.out.println(currentLine);
  243.                         currentLine = addition;
  244.                 } else {
  245.                         currentLine += addition;
  246.                 }
  247.         }
  248.         System.out.print(currentLine);
  249.         System.out.println(greatestPrimeAsSmallAs(Integer.MAX_VALUE));
  250.     }
  251.  
  252.     public static boolean isPrime(int candidate) {
  253.         int sqrt = (int) Math.sqrt(candidate) + 1;
  254.         for (int i = 2; i <= sqrt; ++i) {
  255.                 if (candidate % i == 0) {
  256.                         return false;
  257.                 }
  258.         }
  259.         return true;
  260.     }
  261.  
  262.     public static int leastPrimeAsLargeAs(int target) {
  263.             for (int i = target; i < Integer.MAX_VALUE; ++i) {
  264.                     if (isPrime(i))
  265.                             return i;
  266.             }
  267.             return 0;
  268.     }
  269.     public static int greatestPrimeAsSmallAs(int target) {
  270.             for (int i = target; i > 0 ; --i) {
  271.                     if (isPrime(i))
  272.                             return i;
  273.             }
  274.             return 0;
  275.     }
  276.     //*/
  277. }
  278.  
  279.