1. /* ====================================================================
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowledgement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowledgement may appear in the software itself,
  24. * if and wherever such third-party acknowledgements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Commons", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Software Foundation.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. /*
  55. * Note: originally released under the GNU LGPL v2.1,
  56. * but rereleased by the original author under the ASF license (above).
  57. */
  58. package org.apache.commons.lang;
  59. /**
  60. * <p>A hash map that uses primitive ints for the key rather than objects.</p>
  61. *
  62. * <p>Note that this class is for internal optimization purposes only, and may
  63. * not be supported in future releases of Jakarta Commons Lang. Utilities of
  64. * this sort may be included in future releases of Jakarta Commons Collections.</p>
  65. *
  66. * @author Justin Couch
  67. * @author Alex Chaffee (alex@apache.org)
  68. * @author Stephen Colebourne
  69. * @since 2.0
  70. * @version $Revision: 1.5 $
  71. * @see java.util.HashMap
  72. */
  73. class IntHashMap {
  74. /**
  75. * The hash table data.
  76. */
  77. private transient Entry table[];
  78. /**
  79. * The total number of entries in the hash table.
  80. */
  81. private transient int count;
  82. /**
  83. * The table is rehashed when its size exceeds this threshold. (The
  84. * value of this field is (int)(capacity * loadFactor).)
  85. *
  86. * @serial
  87. */
  88. private int threshold;
  89. /**
  90. * The load factor for the hashtable.
  91. *
  92. * @serial
  93. */
  94. private float loadFactor;
  95. /**
  96. * <p>Innerclass that acts as a datastructure to create a new entry in the
  97. * table.</p>
  98. */
  99. private static class Entry {
  100. int hash;
  101. int key;
  102. Object value;
  103. Entry next;
  104. /**
  105. * <p>Create a new entry with the given values.</p>
  106. *
  107. * @param hash The code used to hash the object with
  108. * @param key The key used to enter this in the table
  109. * @param value The value for this key
  110. * @param next A reference to the next entry in the table
  111. */
  112. protected Entry(int hash, int key, Object value, Entry next) {
  113. this.hash = hash;
  114. this.key = key;
  115. this.value = value;
  116. this.next = next;
  117. }
  118. }
  119. /**
  120. * <p>Constructs a new, empty hashtable with a default capacity and load
  121. * factor, which is <code>20</code> and <code>0.75</code> respectively.</p>
  122. */
  123. public IntHashMap() {
  124. this(20, 0.75f);
  125. }
  126. /**
  127. * <p>Constructs a new, empty hashtable with the specified initial capacity
  128. * and default load factor, which is <code>0.75</code>.</p>
  129. *
  130. * @param initialCapacity the initial capacity of the hashtable.
  131. * @throws IllegalArgumentException if the initial capacity is less
  132. * than zero.
  133. */
  134. public IntHashMap(int initialCapacity) {
  135. this(initialCapacity, 0.75f);
  136. }
  137. /**
  138. * <p>Constructs a new, empty hashtable with the specified initial
  139. * capacity and the specified load factor.</p>
  140. *
  141. * @param initialCapacity the initial capacity of the hashtable.
  142. * @param loadFactor the load factor of the hashtable.
  143. * @throws IllegalArgumentException if the initial capacity is less
  144. * than zero, or if the load factor is nonpositive.
  145. */
  146. public IntHashMap(int initialCapacity, float loadFactor) {
  147. super();
  148. if (initialCapacity < 0) {
  149. throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
  150. }
  151. if (loadFactor <= 0) {
  152. throw new IllegalArgumentException("Illegal Load: " + loadFactor);
  153. }
  154. if (initialCapacity == 0) {
  155. initialCapacity = 1;
  156. }
  157. this.loadFactor = loadFactor;
  158. table = new Entry[initialCapacity];
  159. threshold = (int) (initialCapacity * loadFactor);
  160. }
  161. /**
  162. * <p>Returns the number of keys in this hashtable.</p>
  163. *
  164. * @return the number of keys in this hashtable.
  165. */
  166. public int size() {
  167. return count;
  168. }
  169. /**
  170. * <p>Tests if this hashtable maps no keys to values.</p>
  171. *
  172. * @return <code>true</code> if this hashtable maps no keys to values;
  173. * <code>false</code> otherwise.
  174. */
  175. public boolean isEmpty() {
  176. return count == 0;
  177. }
  178. /**
  179. * <p>Tests if some key maps into the specified value in this hashtable.
  180. * This operation is more expensive than the <code>containsKey</code>
  181. * method.</p>
  182. *
  183. * <p>Note that this method is identical in functionality to containsValue,
  184. * (which is part of the Map interface in the collections framework).</p>
  185. *
  186. * @param value a value to search for.
  187. * @return <code>true</code> if and only if some key maps to the
  188. * <code>value</code> argument in this hashtable as
  189. * determined by the <tt>equals</tt> method;
  190. * <code>false</code> otherwise.
  191. * @throws NullPointerException if the value is <code>null</code>.
  192. * @see #containsKey(int)
  193. * @see #containsValue(Object)
  194. * @see java.util.Map
  195. */
  196. public boolean contains(Object value) {
  197. if (value == null) {
  198. throw new NullPointerException();
  199. }
  200. Entry tab[] = table;
  201. for (int i = tab.length; i-- > 0;) {
  202. for (Entry e = tab[i]; e != null; e = e.next) {
  203. if (e.value.equals(value)) {
  204. return true;
  205. }
  206. }
  207. }
  208. return false;
  209. }
  210. /**
  211. * <p>Returns <code>true</code> if this HashMap maps one or more keys
  212. * to this value.</p>
  213. *
  214. * <p>Note that this method is identical in functionality to contains
  215. * (which predates the Map interface).</p>
  216. *
  217. * @param value value whose presence in this HashMap is to be tested.
  218. * @see java.util.Map
  219. * @since JDK1.2
  220. */
  221. public boolean containsValue(Object value) {
  222. return contains(value);
  223. }
  224. /**
  225. * <p>Tests if the specified object is a key in this hashtable.</p>
  226. *
  227. * @param key possible key.
  228. * @return <code>true</code> if and only if the specified object is a
  229. * key in this hashtable, as determined by the <tt>equals</tt>
  230. * method; <code>false</code> otherwise.
  231. * @see #contains(Object)
  232. */
  233. public boolean containsKey(int key) {
  234. Entry tab[] = table;
  235. int hash = key;
  236. int index = (hash & 0x7FFFFFFF) % tab.length;
  237. for (Entry e = tab[index]; e != null; e = e.next) {
  238. if (e.hash == hash) {
  239. return true;
  240. }
  241. }
  242. return false;
  243. }
  244. /**
  245. * <p>Returns the value to which the specified key is mapped in this map.</p>
  246. *
  247. * @param key a key in the hashtable.
  248. * @return the value to which the key is mapped in this hashtable;
  249. * <code>null</code> if the key is not mapped to any value in
  250. * this hashtable.
  251. * @see #put(int, Object)
  252. */
  253. public Object get(int key) {
  254. Entry tab[] = table;
  255. int hash = key;
  256. int index = (hash & 0x7FFFFFFF) % tab.length;
  257. for (Entry e = tab[index]; e != null; e = e.next) {
  258. if (e.hash == hash) {
  259. return e.value;
  260. }
  261. }
  262. return null;
  263. }
  264. /**
  265. * <p>Increases the capacity of and internally reorganizes this
  266. * hashtable, in order to accommodate and access its entries more
  267. * efficiently.</p>
  268. *
  269. * <p>This method is called automatically when the number of keys
  270. * in the hashtable exceeds this hashtable's capacity and load
  271. * factor.</p>
  272. */
  273. protected void rehash() {
  274. int oldCapacity = table.length;
  275. Entry oldMap[] = table;
  276. int newCapacity = oldCapacity * 2 + 1;
  277. Entry newMap[] = new Entry[newCapacity];
  278. threshold = (int) (newCapacity * loadFactor);
  279. table = newMap;
  280. for (int i = oldCapacity; i-- > 0;) {
  281. for (Entry old = oldMap[i]; old != null;) {
  282. Entry e = old;
  283. old = old.next;
  284. int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  285. e.next = newMap[index];
  286. newMap[index] = e;
  287. }
  288. }
  289. }
  290. /**
  291. * <p>Maps the specified <code>key</code> to the specified
  292. * <code>value</code> in this hashtable. The key cannot be
  293. * <code>null</code>. </p>
  294. *
  295. * <p>The value can be retrieved by calling the <code>get</code> method
  296. * with a key that is equal to the original key.</p>
  297. *
  298. * @param key the hashtable key.
  299. * @param value the value.
  300. * @return the previous value of the specified key in this hashtable,
  301. * or <code>null</code> if it did not have one.
  302. * @throws NullPointerException if the key is <code>null</code>.
  303. * @see #get(int)
  304. */
  305. public Object put(int key, Object value) {
  306. // Makes sure the key is not already in the hashtable.
  307. Entry tab[] = table;
  308. int hash = key;
  309. int index = (hash & 0x7FFFFFFF) % tab.length;
  310. for (Entry e = tab[index]; e != null; e = e.next) {
  311. if (e.hash == hash) {
  312. Object old = e.value;
  313. e.value = value;
  314. return old;
  315. }
  316. }
  317. if (count >= threshold) {
  318. // Rehash the table if the threshold is exceeded
  319. rehash();
  320. tab = table;
  321. index = (hash & 0x7FFFFFFF) % tab.length;
  322. }
  323. // Creates the new entry.
  324. Entry e = new Entry(hash, key, value, tab[index]);
  325. tab[index] = e;
  326. count++;
  327. return null;
  328. }
  329. /**
  330. * <p>Removes the key (and its corresponding value) from this
  331. * hashtable.</p>
  332. *
  333. * <p>This method does nothing if the key is not present in the
  334. * hashtable.</p>
  335. *
  336. * @param key the key that needs to be removed.
  337. * @return the value to which the key had been mapped in this hashtable,
  338. * or <code>null</code> if the key did not have a mapping.
  339. */
  340. public Object remove(int key) {
  341. Entry tab[] = table;
  342. int hash = key;
  343. int index = (hash & 0x7FFFFFFF) % tab.length;
  344. for (Entry e = tab[index], prev = null; e != null; prev = e, e = e.next) {
  345. if (e.hash == hash) {
  346. if (prev != null) {
  347. prev.next = e.next;
  348. } else {
  349. tab[index] = e.next;
  350. }
  351. count--;
  352. Object oldValue = e.value;
  353. e.value = null;
  354. return oldValue;
  355. }
  356. }
  357. return null;
  358. }
  359. /**
  360. * <p>Clears this hashtable so that it contains no keys.</p>
  361. */
  362. public synchronized void clear() {
  363. Entry tab[] = table;
  364. for (int index = tab.length; --index >= 0;) {
  365. tab[index] = null;
  366. }
  367. count = 0;
  368. }
  369. }