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