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