PageRenderTime 45ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/jboss-as-7.1.1.Final/controller/src/main/java/org/jboss/as/controller/registry/FastCopyHashMap.java

#
Java | 759 lines | 524 code | 155 blank | 80 comment | 130 complexity | ae917ae03c3d2b28ac57d2a82b476def MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0
  1. /*
  2. * JBoss, Home of Professional Open Source.
  3. * Copyright 2011, Red Hat, Inc., and individual contributors
  4. * as indicated by the @author tags. See the copyright.txt file in the
  5. * distribution for a full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.jboss.as.controller.registry;
  23. import java.io.IOException;
  24. import java.io.Serializable;
  25. import java.util.AbstractCollection;
  26. import java.util.AbstractMap;
  27. import java.util.AbstractSet;
  28. import java.util.Collection;
  29. import java.util.ConcurrentModificationException;
  30. import java.util.Iterator;
  31. import java.util.Map;
  32. import java.util.NoSuchElementException;
  33. import java.util.Set;
  34. import static org.jboss.as.controller.ControllerMessages.MESSAGES;
  35. /**
  36. * A HashMap that is optimized for fast shallow copies. If the copy-ctor is
  37. * passed another FastCopyHashMap, or clone is called on this map, the shallow
  38. * copy can be performed using little more than a single array copy. In order to
  39. * accomplish this, immutable objects must be used internally, so update
  40. * operations result in slightly more object churn than <code>HashMap</code>.
  41. * <p/>
  42. * Note: It is very important to use a smaller load factor than you normally
  43. * would for HashMap, since the implementation is open-addressed with linear
  44. * probing. With a 50% load-factor a get is expected to return in only 2 probes.
  45. * However, a 90% load-factor is expected to return in around 50 probes.
  46. *
  47. * @author Jason T. Greene
  48. */
  49. class FastCopyHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable {
  50. /**
  51. * Marks null keys.
  52. */
  53. private static final Object NULL = new Object();
  54. /**
  55. * Serialization ID
  56. */
  57. private static final long serialVersionUID = 10929568968762L;
  58. /**
  59. * Same default as HashMap, must be a power of 2
  60. */
  61. private static final int DEFAULT_CAPACITY = 8;
  62. /**
  63. * MAX_INT - 1
  64. */
  65. private static final int MAXIMUM_CAPACITY = 1 << 30;
  66. /**
  67. * 67%, just like IdentityHashMap
  68. */
  69. private static final float DEFAULT_LOAD_FACTOR = 0.67f;
  70. /**
  71. * The open-addressed table
  72. */
  73. private transient Entry<K, V>[] table;
  74. /**
  75. * The current number of key-value pairs
  76. */
  77. private transient int size;
  78. /**
  79. * The next resize
  80. */
  81. private transient int threshold;
  82. /**
  83. * The user defined load factor which defines when to resize
  84. */
  85. private final float loadFactor;
  86. /**
  87. * Counter used to detect changes made outside of an iterator
  88. */
  89. private transient int modCount;
  90. // Cached views
  91. private transient KeySet keySet;
  92. private transient Values values;
  93. private transient EntrySet entrySet;
  94. public FastCopyHashMap(int initialCapacity, float loadFactor) {
  95. if (initialCapacity < 0)
  96. throw MESSAGES.invalidTableSize();
  97. if (initialCapacity > MAXIMUM_CAPACITY)
  98. initialCapacity = MAXIMUM_CAPACITY;
  99. if (!(loadFactor > 0F && loadFactor <= 1F))
  100. throw MESSAGES.invalidLoadFactor();
  101. this.loadFactor = loadFactor;
  102. init(initialCapacity, loadFactor);
  103. }
  104. @SuppressWarnings("unchecked")
  105. public FastCopyHashMap(Map<? extends K, ? extends V> map) {
  106. if (map instanceof FastCopyHashMap) {
  107. FastCopyHashMap<? extends K, ? extends V> fast = (FastCopyHashMap<? extends K, ? extends V>) map;
  108. this.table = (Entry<K, V>[]) fast.table.clone();
  109. this.loadFactor = fast.loadFactor;
  110. this.size = fast.size;
  111. this.threshold = fast.threshold;
  112. } else {
  113. this.loadFactor = DEFAULT_LOAD_FACTOR;
  114. init(map.size(), this.loadFactor);
  115. putAll(map);
  116. }
  117. }
  118. @SuppressWarnings("unchecked")
  119. private void init(int initialCapacity, float loadFactor) {
  120. int c = 1;
  121. while (c < initialCapacity) c <<= 1;
  122. threshold = (int) (c * loadFactor);
  123. // Include the load factor when sizing the table for the first time
  124. if (initialCapacity > threshold && c < MAXIMUM_CAPACITY) {
  125. c <<= 1;
  126. threshold = (int) (c * loadFactor);
  127. }
  128. this.table = (Entry<K, V>[]) new Entry[c];
  129. }
  130. public FastCopyHashMap(int initialCapacity) {
  131. this(initialCapacity, DEFAULT_LOAD_FACTOR);
  132. }
  133. public FastCopyHashMap() {
  134. this(DEFAULT_CAPACITY);
  135. }
  136. // The normal bit spreader...
  137. private static int hash(Object key) {
  138. int h = key.hashCode();
  139. h ^= (h >>> 20) ^ (h >>> 12);
  140. return h ^ (h >>> 7) ^ (h >>> 4);
  141. }
  142. @SuppressWarnings("unchecked")
  143. private static <K> K maskNull(K key) {
  144. return key == null ? (K) NULL : key;
  145. }
  146. private static <K> K unmaskNull(K key) {
  147. return key == NULL ? null : key;
  148. }
  149. private int nextIndex(int index, int length) {
  150. index = (index >= length - 1) ? 0 : index + 1;
  151. return index;
  152. }
  153. private static boolean eq(Object o1, Object o2) {
  154. return o1 == o2 || (o1 != null && o1.equals(o2));
  155. }
  156. private static int index(int hashCode, int length) {
  157. return hashCode & (length - 1);
  158. }
  159. public int size() {
  160. return size;
  161. }
  162. public boolean isEmpty() {
  163. return size == 0;
  164. }
  165. public V get(Object key) {
  166. key = maskNull(key);
  167. int hash = hash(key);
  168. int length = table.length;
  169. int index = index(hash, length);
  170. for (int start = index; ;) {
  171. Entry<K, V> e = table[index];
  172. if (e == null)
  173. return null;
  174. if (e.hash == hash && eq(key, e.key))
  175. return e.value;
  176. index = nextIndex(index, length);
  177. if (index == start) // Full table
  178. return null;
  179. }
  180. }
  181. public boolean containsKey(Object key) {
  182. key = maskNull(key);
  183. int hash = hash(key);
  184. int length = table.length;
  185. int index = index(hash, length);
  186. for (int start = index; ;) {
  187. Entry<K, V> e = table[index];
  188. if (e == null)
  189. return false;
  190. if (e.hash == hash && eq(key, e.key))
  191. return true;
  192. index = nextIndex(index, length);
  193. if (index == start) // Full table
  194. return false;
  195. }
  196. }
  197. public boolean containsValue(Object value) {
  198. for (Entry<K, V> e : table)
  199. if (e != null && eq(value, e.value))
  200. return true;
  201. return false;
  202. }
  203. public V put(K key, V value) {
  204. key = maskNull(key);
  205. Entry<K, V>[] table = this.table;
  206. int hash = hash(key);
  207. int length = table.length;
  208. int index = index(hash, length);
  209. for (int start = index; ;) {
  210. Entry<K, V> e = table[index];
  211. if (e == null)
  212. break;
  213. if (e.hash == hash && eq(key, e.key)) {
  214. table[index] = new Entry<K, V>(e.key, e.hash, value);
  215. return e.value;
  216. }
  217. index = nextIndex(index, length);
  218. if (index == start)
  219. throw MESSAGES.tableIsFull();
  220. }
  221. modCount++;
  222. table[index] = new Entry<K, V>(key, hash, value);
  223. if (++size >= threshold)
  224. resize(length);
  225. return null;
  226. }
  227. @SuppressWarnings("unchecked")
  228. private void resize(int from) {
  229. int newLength = from << 1;
  230. // Can't get any bigger
  231. if (newLength > MAXIMUM_CAPACITY || newLength <= from)
  232. return;
  233. Entry<K, V>[] newTable = new Entry[newLength];
  234. Entry<K, V>[] old = table;
  235. for (Entry<K, V> e : old) {
  236. if (e == null)
  237. continue;
  238. int index = index(e.hash, newLength);
  239. while (newTable[index] != null)
  240. index = nextIndex(index, newLength);
  241. newTable[index] = e;
  242. }
  243. threshold = (int) (loadFactor * newLength);
  244. table = newTable;
  245. }
  246. public void putAll(Map<? extends K, ? extends V> map) {
  247. int size = map.size();
  248. if (size == 0)
  249. return;
  250. if (size > threshold) {
  251. if (size > MAXIMUM_CAPACITY)
  252. size = MAXIMUM_CAPACITY;
  253. int length = table.length;
  254. while (length < size) length <<= 1;
  255. resize(length);
  256. }
  257. for (Map.Entry<? extends K, ? extends V> e : map.entrySet())
  258. put(e.getKey(), e.getValue());
  259. }
  260. public V remove(Object key) {
  261. key = maskNull(key);
  262. Entry<K, V>[] table = this.table;
  263. int length = table.length;
  264. int hash = hash(key);
  265. int start = index(hash, length);
  266. for (int index = start; ;) {
  267. Entry<K, V> e = table[index];
  268. if (e == null)
  269. return null;
  270. if (e.hash == hash && eq(key, e.key)) {
  271. table[index] = null;
  272. relocate(index);
  273. modCount++;
  274. size--;
  275. return e.value;
  276. }
  277. index = nextIndex(index, length);
  278. if (index == start)
  279. return null;
  280. }
  281. }
  282. private void relocate(int start) {
  283. Entry<K, V>[] table = this.table;
  284. int length = table.length;
  285. int current = nextIndex(start, length);
  286. for (; ;) {
  287. Entry<K, V> e = table[current];
  288. if (e == null)
  289. return;
  290. // A Doug Lea variant of Knuth's Section 6.4 Algorithm R.
  291. // This provides a non-recursive method of relocating
  292. // entries to their optimal positions once a gap is created.
  293. int prefer = index(e.hash, length);
  294. if ((current < prefer && (prefer <= start || start <= current))
  295. || (prefer <= start && start <= current)) {
  296. table[start] = e;
  297. table[current] = null;
  298. start = current;
  299. }
  300. current = nextIndex(current, length);
  301. }
  302. }
  303. public void clear() {
  304. modCount++;
  305. Entry<K, V>[] table = this.table;
  306. for (int i = 0; i < table.length; i++)
  307. table[i] = null;
  308. size = 0;
  309. }
  310. @SuppressWarnings("unchecked")
  311. public FastCopyHashMap<K, V> clone() {
  312. try {
  313. FastCopyHashMap<K, V> clone = (FastCopyHashMap<K, V>) super.clone();
  314. clone.table = table.clone();
  315. clone.entrySet = null;
  316. clone.values = null;
  317. clone.keySet = null;
  318. return clone;
  319. }
  320. catch (CloneNotSupportedException e) {
  321. // should never happen
  322. throw new IllegalStateException(e);
  323. }
  324. }
  325. public void printDebugStats() {
  326. int optimal = 0;
  327. int total = 0;
  328. int totalSkew = 0;
  329. int maxSkew = 0;
  330. for (int i = 0; i < table.length; i++) {
  331. Entry<K, V> e = table[i];
  332. if (e != null) {
  333. total++;
  334. int target = index(e.hash, table.length);
  335. if (i == target)
  336. optimal++;
  337. else {
  338. int skew = Math.abs(i - target);
  339. if (skew > maxSkew) maxSkew = skew;
  340. totalSkew += skew;
  341. }
  342. }
  343. }
  344. System.out.println(" Size: " + size);
  345. System.out.println(" Real Size: " + total);
  346. System.out.println(" Optimal: " + optimal + " (" + (float) optimal * 100 / total + "%)");
  347. System.out.println(" Average Distnce: " + ((float) totalSkew / (total - optimal)));
  348. System.out.println(" Max Distance: " + maxSkew);
  349. }
  350. public Set<Map.Entry<K, V>> entrySet() {
  351. if (entrySet == null)
  352. entrySet = new EntrySet();
  353. return entrySet;
  354. }
  355. public Set<K> keySet() {
  356. if (keySet == null)
  357. keySet = new KeySet();
  358. return keySet;
  359. }
  360. public Collection<V> values() {
  361. if (values == null)
  362. values = new Values();
  363. return values;
  364. }
  365. @SuppressWarnings("unchecked")
  366. private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
  367. s.defaultReadObject();
  368. int size = s.readInt();
  369. init(size, loadFactor);
  370. for (int i = 0; i < size; i++) {
  371. K key = (K) s.readObject();
  372. V value = (V) s.readObject();
  373. putForCreate(key, value);
  374. }
  375. this.size = size;
  376. }
  377. @SuppressWarnings("unchecked")
  378. private void putForCreate(K key, V value) {
  379. key = maskNull(key);
  380. Entry<K, V>[] table = this.table;
  381. int hash = hash(key);
  382. int length = table.length;
  383. int index = index(hash, length);
  384. Entry<K, V> e = table[index];
  385. while (e != null) {
  386. index = nextIndex(index, length);
  387. e = table[index];
  388. }
  389. table[index] = new Entry<K, V>(key, hash, value);
  390. }
  391. private void writeObject(java.io.ObjectOutputStream s) throws IOException {
  392. s.defaultWriteObject();
  393. s.writeInt(size);
  394. for (Entry<K, V> e : table) {
  395. if (e != null) {
  396. s.writeObject(unmaskNull(e.key));
  397. s.writeObject(e.value);
  398. }
  399. }
  400. }
  401. private static final class Entry<K, V> {
  402. final K key;
  403. final int hash;
  404. final V value;
  405. Entry(K key, int hash, V value) {
  406. this.key = key;
  407. this.hash = hash;
  408. this.value = value;
  409. }
  410. }
  411. private abstract class FasyCopyHashMapIterator<E> implements Iterator<E> {
  412. private int next = 0;
  413. private int expectedCount = modCount;
  414. private int current = -1;
  415. private boolean hasNext;
  416. Entry<K, V>[] table = FastCopyHashMap.this.table;
  417. public boolean hasNext() {
  418. if (hasNext == true)
  419. return true;
  420. Entry<K, V>[] table = this.table;
  421. for (int i = next; i < table.length; i++) {
  422. if (table[i] != null) {
  423. next = i;
  424. return hasNext = true;
  425. }
  426. }
  427. next = table.length;
  428. return false;
  429. }
  430. protected Entry<K, V> nextEntry() {
  431. if (modCount != expectedCount)
  432. throw new ConcurrentModificationException();
  433. if (!hasNext && !hasNext())
  434. throw new NoSuchElementException();
  435. current = next++;
  436. hasNext = false;
  437. return table[current];
  438. }
  439. @SuppressWarnings("unchecked")
  440. public void remove() {
  441. if (modCount != expectedCount)
  442. throw new ConcurrentModificationException();
  443. int current = this.current;
  444. int delete = current;
  445. if (current == -1)
  446. throw new IllegalStateException();
  447. // Invalidate current (prevents multiple remove)
  448. this.current = -1;
  449. // Start were we relocate
  450. next = delete;
  451. Entry<K, V>[] table = this.table;
  452. if (table != FastCopyHashMap.this.table) {
  453. FastCopyHashMap.this.remove(table[delete].key);
  454. table[delete] = null;
  455. expectedCount = modCount;
  456. return;
  457. }
  458. int length = table.length;
  459. int i = delete;
  460. table[delete] = null;
  461. size--;
  462. for (; ;) {
  463. i = nextIndex(i, length);
  464. Entry<K, V> e = table[i];
  465. if (e == null)
  466. break;
  467. int prefer = index(e.hash, length);
  468. if ((i < prefer && (prefer <= delete || delete <= i))
  469. || (prefer <= delete && delete <= i)) {
  470. // Snapshot the unseen portion of the table if we have
  471. // to relocate an entry that was already seen by this iterator
  472. if (i < current && current <= delete && table == FastCopyHashMap.this.table) {
  473. int remaining = length - current;
  474. Entry<K, V>[] newTable = (Entry<K, V>[]) new Entry[remaining];
  475. System.arraycopy(table, current, newTable, 0, remaining);
  476. // Replace iterator's table.
  477. // Leave table local var pointing to the real table
  478. this.table = newTable;
  479. next = 0;
  480. }
  481. // Do the swap on the real table
  482. table[delete] = e;
  483. table[i] = null;
  484. delete = i;
  485. }
  486. }
  487. }
  488. }
  489. private class KeyIterator extends FasyCopyHashMapIterator<K> {
  490. public K next() {
  491. return unmaskNull(nextEntry().key);
  492. }
  493. }
  494. private class ValueIterator extends FasyCopyHashMapIterator<V> {
  495. public V next() {
  496. return nextEntry().value;
  497. }
  498. }
  499. private class EntryIterator extends FasyCopyHashMapIterator<Map.Entry<K, V>> {
  500. private class WriteThroughEntry extends SimpleEntry<K, V> {
  501. WriteThroughEntry(K key, V value) {
  502. super(key, value);
  503. }
  504. public V setValue(V value) {
  505. if (table != FastCopyHashMap.this.table)
  506. FastCopyHashMap.this.put(getKey(), value);
  507. return super.setValue(value);
  508. }
  509. }
  510. public Map.Entry<K, V> next() {
  511. Entry<K, V> e = nextEntry();
  512. return new WriteThroughEntry(unmaskNull(e.key), e.value);
  513. }
  514. }
  515. private class KeySet extends AbstractSet<K> {
  516. public Iterator<K> iterator() {
  517. return new KeyIterator();
  518. }
  519. public void clear() {
  520. FastCopyHashMap.this.clear();
  521. }
  522. public boolean contains(Object o) {
  523. return containsKey(o);
  524. }
  525. public boolean remove(Object o) {
  526. int size = size();
  527. FastCopyHashMap.this.remove(o);
  528. return size() < size;
  529. }
  530. public int size() {
  531. return FastCopyHashMap.this.size();
  532. }
  533. }
  534. private class Values extends AbstractCollection<V> {
  535. public Iterator<V> iterator() {
  536. return new ValueIterator();
  537. }
  538. public void clear() {
  539. FastCopyHashMap.this.clear();
  540. }
  541. public int size() {
  542. return FastCopyHashMap.this.size();
  543. }
  544. }
  545. private class EntrySet extends AbstractSet<Map.Entry<K, V>> {
  546. public Iterator<Map.Entry<K, V>> iterator() {
  547. return new EntryIterator();
  548. }
  549. public boolean contains(Object o) {
  550. if (!(o instanceof Map.Entry))
  551. return false;
  552. Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
  553. Object value = get(entry.getKey());
  554. return eq(entry.getValue(), value);
  555. }
  556. public void clear() {
  557. FastCopyHashMap.this.clear();
  558. }
  559. public boolean isEmpty() {
  560. return FastCopyHashMap.this.isEmpty();
  561. }
  562. public int size() {
  563. return FastCopyHashMap.this.size();
  564. }
  565. }
  566. protected static class SimpleEntry<K, V> implements Map.Entry<K, V> {
  567. private K key;
  568. private V value;
  569. SimpleEntry(K key, V value) {
  570. this.key = key;
  571. this.value = value;
  572. }
  573. SimpleEntry(Map.Entry<K, V> entry) {
  574. this.key = entry.getKey();
  575. this.value = entry.getValue();
  576. }
  577. public K getKey() {
  578. return key;
  579. }
  580. public V getValue() {
  581. return value;
  582. }
  583. public V setValue(V value) {
  584. V old = this.value;
  585. this.value = value;
  586. return old;
  587. }
  588. public boolean equals(Object o) {
  589. if (this == o)
  590. return true;
  591. if (!(o instanceof Map.Entry))
  592. return false;
  593. Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
  594. return eq(key, e.getKey()) && eq(value, e.getValue());
  595. }
  596. public int hashCode() {
  597. return (key == null ? 0 : hash(key)) ^
  598. (value == null ? 0 : hash(value));
  599. }
  600. public String toString() {
  601. return getKey() + "=" + getValue();
  602. }
  603. }
  604. }