/hazelcast/src/main/java/com/hazelcast/util/SortedHashMap.java

https://bitbucket.org/gabral6_gmailcom/hazelcast · Java · 550 lines · 447 code · 77 blank · 26 comment · 113 complexity · 7cd8f28c2137b6a85ed4ddacf722b168 MD5 · raw file

  1. /*
  2. * Copyright (c) 2008-2013, Hazelcast, Inc. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.hazelcast.util;
  17. import java.util.*;
  18. public class SortedHashMap<K, V> extends AbstractMap<K, V> {
  19. static final int MAXIMUM_CAPACITY = 1 << 30;
  20. static final int DEFAULT_INITIAL_CAPACITY = 16;
  21. static final float DEFAULT_LOAD_FACTOR = 0.75f;
  22. int modCount = 0;
  23. Entry<K, V>[] table;
  24. private transient Entry<K, V> header = null;
  25. int size;
  26. int threshold;
  27. final float loadFactor;
  28. final OrderingType orderingType;
  29. public enum OrderingType {
  30. NONE, LRU, LFU, HASH
  31. }
  32. public SortedHashMap() {
  33. this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, OrderingType.NONE);
  34. }
  35. public SortedHashMap(int initialCapacity) {
  36. this(initialCapacity, DEFAULT_LOAD_FACTOR, OrderingType.NONE);
  37. }
  38. public SortedHashMap(int initialCapacity, OrderingType orderingType) {
  39. this(initialCapacity, DEFAULT_LOAD_FACTOR, orderingType);
  40. }
  41. public SortedHashMap(int initialCapacity, float loadFactor, OrderingType orderingType) {
  42. if (initialCapacity < 0)
  43. throw new IllegalArgumentException("Illegal initial capacity: " +
  44. initialCapacity);
  45. if (initialCapacity > MAXIMUM_CAPACITY)
  46. initialCapacity = MAXIMUM_CAPACITY;
  47. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  48. throw new IllegalArgumentException("Illegal load factor: " +
  49. loadFactor);
  50. // Find a power of 2 >= initialCapacity
  51. int capacity = 1;
  52. while (capacity < initialCapacity)
  53. capacity <<= 1;
  54. this.orderingType = orderingType;
  55. this.loadFactor = loadFactor;
  56. threshold = (int) (capacity * loadFactor);
  57. table = new Entry[capacity];
  58. header = new Entry<K, V>(-1, null, null, null);
  59. header.before = header.after = header;
  60. }
  61. public V put(K key, V value) {
  62. int hash = hash(key.hashCode());
  63. int i = indexFor(hash, table.length);
  64. for (Entry<K, V> e = table[i]; e != null; e = e.next) {
  65. Object k;
  66. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
  67. V oldValue = e.value;
  68. e.value = value;
  69. e.recordAccess(this);
  70. return oldValue;
  71. }
  72. }
  73. modCount++;
  74. addEntry(hash, key, value, i);
  75. return null;
  76. }
  77. public static void touch(SortedHashMap linkedMap, Object key, OrderingType orderingType) {
  78. Entry e = linkedMap.getEntry(key);
  79. if (e != null) {
  80. e.touch(linkedMap, orderingType);
  81. }
  82. }
  83. public static void moveToTop(SortedHashMap linkedMap, Object key) {
  84. Entry e = linkedMap.getEntry(key);
  85. if (e != null) {
  86. e.moveToTop(linkedMap);
  87. }
  88. }
  89. public static OrderingType getOrderingTypeByName(String orderingType) {
  90. return OrderingType.valueOf(orderingType.toUpperCase());
  91. }
  92. public boolean containsKey(Object key) {
  93. return getEntry(key) != null;
  94. }
  95. public V get(Object key) {
  96. Entry<K, V> e = getEntry(key);
  97. if (e == null)
  98. return null;
  99. e.recordAccess(this);
  100. return e.value;
  101. }
  102. Entry<K, V> getEntry(Object k) {
  103. int hash = hash(k.hashCode());
  104. int i = indexFor(hash, table.length);
  105. Entry<K, V> e = table[i];
  106. while (e != null && !(e.hash == hash && eq(k, e.key)))
  107. e = e.next;
  108. return e;
  109. }
  110. static boolean eq(Object x, Object y) {
  111. return x == y || x.equals(y);
  112. }
  113. public void clear() {
  114. modCount++;
  115. Entry[] tab = table;
  116. for (int i = 0; i < tab.length; i++)
  117. tab[i] = null;
  118. size = 0;
  119. header.before = header.after = header;
  120. }
  121. public V remove(Object key) {
  122. Entry<K, V> e = removeEntryForKey(key);
  123. return (e == null ? null : e.value);
  124. }
  125. Entry<K, V> removeEntryForKey(Object k) {
  126. int hash = hash(k.hashCode());
  127. int i = indexFor(hash, table.length);
  128. Entry<K, V> prev = table[i];
  129. Entry<K, V> e = prev;
  130. while (e != null) {
  131. Entry<K, V> next = e.next;
  132. if ((e.hash == hash) && (k == e.key || k.equals(e.key))) {
  133. modCount++;
  134. size--;
  135. if (prev == e)
  136. table[i] = next;
  137. else
  138. prev.next = next;
  139. e.recordRemoval(this);
  140. return e;
  141. }
  142. prev = e;
  143. e = next;
  144. }
  145. return e;
  146. }
  147. Entry<K, V> removeMapping(Object o) {
  148. if (!(o instanceof Map.Entry))
  149. return null;
  150. Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
  151. Object k = entry.getKey();
  152. int hash = hash(k.hashCode());
  153. int i = indexFor(hash, table.length);
  154. Entry<K, V> prev = table[i];
  155. Entry<K, V> e = prev;
  156. while (e != null) {
  157. Entry<K, V> next = e.next;
  158. if (e.hash == hash && e.equals(entry)) {
  159. modCount++;
  160. size--;
  161. if (prev == e)
  162. table[i] = next;
  163. else
  164. prev.next = next;
  165. e.recordRemoval(this);
  166. return e;
  167. }
  168. prev = e;
  169. e = next;
  170. }
  171. return e;
  172. }
  173. void addEntry(int hash, K key, V value, int bucketIndex) {
  174. createEntry(hash, key, value, bucketIndex);
  175. Entry<K, V> eldest = header.after;
  176. if (removeEldestEntry(eldest)) {
  177. removeEntryForKey(eldest.key);
  178. } else {
  179. if (size >= threshold)
  180. resize(2 * table.length);
  181. }
  182. }
  183. protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
  184. return false;
  185. }
  186. void createEntry(int hash, K key, V value, int bucketIndex) {
  187. Entry<K, V> old = table[bucketIndex];
  188. Entry<K, V> e = new Entry<K, V>(hash, key, value, old);
  189. table[bucketIndex] = e;
  190. e.addBefore(header);
  191. size++;
  192. }
  193. void resize(int newCapacity) {
  194. Entry[] oldTable = table;
  195. int oldCapacity = oldTable.length;
  196. if (oldCapacity == MAXIMUM_CAPACITY) {
  197. threshold = Integer.MAX_VALUE;
  198. return;
  199. }
  200. Entry[] newTable = new Entry[newCapacity];
  201. transfer(newTable);
  202. table = newTable;
  203. threshold = (int) (newCapacity * loadFactor);
  204. }
  205. void transfer(Entry[] newTable) {
  206. int newCapacity = newTable.length;
  207. for (Entry<K, V> e = header.after; e != header; e = e.after) {
  208. int index = indexFor(e.hash, newCapacity);
  209. e.next = newTable[index];
  210. newTable[index] = e;
  211. }
  212. }
  213. static int hash(int h) {
  214. h ^= (h >>> 20) ^ (h >>> 12);
  215. return h ^ (h >>> 7) ^ (h >>> 4);
  216. }
  217. static int indexFor(int h, int length) {
  218. return h & (length - 1);
  219. }
  220. public boolean containsValue(Object value) {
  221. // Overridden to take advantage of faster iterator
  222. if (value == null) {
  223. for (Entry<K, V> e = header.after; e != header; e = e.after)
  224. if (e.value == null)
  225. return true;
  226. } else {
  227. for (Entry<K, V> e = header.after; e != header; e = e.after)
  228. if (value.equals(e.value))
  229. return true;
  230. }
  231. return false;
  232. }
  233. static class Entry<K, V> implements Map.Entry<K, V> {
  234. K key;
  235. V value;
  236. Entry<K, V> next;
  237. Entry<K, V> after;
  238. Entry<K, V> before;
  239. long accessCount = 1;
  240. long lastAccess = 0;
  241. int hash = -1;
  242. Entry(int hash, K key, V value, Entry<K, V> next) {
  243. this.key = key;
  244. this.value = value;
  245. this.hash = hash;
  246. this.next = next;
  247. lastAccess = Clock.currentTimeMillis();
  248. }
  249. public K getKey() {
  250. return key;
  251. }
  252. public V getValue() {
  253. return value;
  254. }
  255. public V setValue(V value) {
  256. this.value = value;
  257. return value;
  258. }
  259. public boolean equals(Object o) {
  260. if (!(o instanceof Map.Entry)) {
  261. return false;
  262. }
  263. Map.Entry e = (Map.Entry) o;
  264. Object k1 = getKey();
  265. Object k2 = e.getKey();
  266. if (k1 == k2 || (k1 != null && k1.equals(k2))) {
  267. Object v1 = getValue();
  268. Object v2 = e.getValue();
  269. if (v1 == v2 || (v1 != null && v1.equals(v2))) {
  270. return true;
  271. }
  272. }
  273. return false;
  274. }
  275. public int hashCode() {
  276. return key.hashCode();
  277. }
  278. public String toString() {
  279. return "Entry key=" + getKey() + ", value=" + getValue();
  280. }
  281. private void remove() {
  282. before.after = after;
  283. after.before = before;
  284. }
  285. private void addBefore(Entry<K, V> existingEntry) {
  286. after = existingEntry;
  287. before = existingEntry.before;
  288. before.after = this;
  289. after.before = this;
  290. }
  291. private void addAfter(Entry<K, V> existingEntry) {
  292. addBefore(existingEntry.after);
  293. }
  294. /**
  295. * This method is invoked by the superclass whenever the value
  296. * of a pre-existing entry is read by Map.get or modified by Map.set.
  297. * If the enclosing Map is access-ordered, it moves the entry
  298. * to the end of the list; otherwise, it does nothing.
  299. *
  300. * @param lm
  301. */
  302. void recordAccess(SortedHashMap<K, V> lm) {
  303. touch(lm, lm.orderingType);
  304. }
  305. void touch(SortedHashMap<K, V> lm, OrderingType orderingType) {
  306. if (orderingType != OrderingType.NONE) {
  307. lastAccess = Clock.currentTimeMillis();
  308. accessCount++;
  309. lm.modCount++;
  310. if (orderingType == OrderingType.LFU) {
  311. moveLFU(lm);
  312. } else if (orderingType == OrderingType.LRU) {
  313. moveLRU(lm);
  314. } else if (orderingType == OrderingType.HASH) {
  315. moveHash(lm);
  316. } else throw new RuntimeException("Unknown orderingType:" + lm.orderingType);
  317. }
  318. }
  319. void moveLRU(SortedHashMap lm) {
  320. remove();
  321. addBefore(lm.header);
  322. }
  323. void moveLFU(SortedHashMap lm) {
  324. Entry<K, V> nextOne = after;
  325. boolean shouldMove = false;
  326. while (nextOne != null && accessCount >= nextOne.accessCount && nextOne != lm.header) {
  327. shouldMove = true;
  328. nextOne = nextOne.after;
  329. }
  330. if (shouldMove) {
  331. remove();
  332. addBefore(nextOne);
  333. }
  334. }
  335. void moveHash(SortedHashMap lm) {
  336. Entry<K, V> nextOne = after;
  337. boolean shouldMove = false;
  338. while (nextOne != null && nextOne != lm.header && value.hashCode() >= nextOne.value.hashCode()) {
  339. shouldMove = true;
  340. nextOne = nextOne.after;
  341. }
  342. if (shouldMove) {
  343. remove();
  344. addBefore(nextOne);
  345. }
  346. }
  347. void moveToTop(SortedHashMap lm) {
  348. remove();
  349. addAfter(lm.header);
  350. }
  351. void recordRemoval(SortedHashMap<K, V> lm) {
  352. remove();
  353. }
  354. }
  355. private abstract class LinkedHashIterator<T> implements Iterator<T> {
  356. Entry<K, V> nextEntry = header.after;
  357. Entry<K, V> lastReturned = null;
  358. int expectedModCount = modCount;
  359. public boolean hasNext() {
  360. return nextEntry != header;
  361. }
  362. public void remove() {
  363. if (lastReturned == null)
  364. throw new IllegalStateException();
  365. if (modCount != expectedModCount)
  366. throw new ConcurrentModificationException();
  367. SortedHashMap.this.remove(lastReturned.key);
  368. lastReturned = null;
  369. expectedModCount = modCount;
  370. }
  371. Entry<K, V> nextEntry() {
  372. if (modCount != expectedModCount)
  373. throw new ConcurrentModificationException();
  374. if (nextEntry == header)
  375. throw new NoSuchElementException();
  376. Entry<K, V> e = lastReturned = nextEntry;
  377. nextEntry = e.after;
  378. return e;
  379. }
  380. }
  381. private class KeyIterator extends LinkedHashIterator<K> {
  382. public K next() {
  383. return nextEntry().getKey();
  384. }
  385. }
  386. private class ValueIterator extends LinkedHashIterator<V> {
  387. public V next() {
  388. return nextEntry().value;
  389. }
  390. }
  391. private class EntryIterator extends LinkedHashIterator<Map.Entry<K, V>> {
  392. public Map.Entry<K, V> next() {
  393. return nextEntry();
  394. }
  395. }
  396. // These Overrides alter the behavior of superclass view iterator() methods
  397. Iterator<K> newKeyIterator() {
  398. return new KeyIterator();
  399. }
  400. Iterator<V> newValueIterator() {
  401. return new ValueIterator();
  402. }
  403. Iterator<Map.Entry<K, V>> newEntryIterator() {
  404. return new EntryIterator();
  405. }
  406. private transient Set<Map.Entry<K, V>> entrySet = null;
  407. transient volatile Set<K> keySet = null;
  408. transient volatile Collection<V> values = null;
  409. public Set<K> keySet() {
  410. Set<K> ks = keySet;
  411. return (ks != null ? ks : (keySet = new KeySet()));
  412. }
  413. private class KeySet extends AbstractSet<K> {
  414. public Iterator<K> iterator() {
  415. return newKeyIterator();
  416. }
  417. public int size() {
  418. return size;
  419. }
  420. public boolean contains(Object o) {
  421. return containsKey(o);
  422. }
  423. public boolean remove(Object o) {
  424. return SortedHashMap.this.removeEntryForKey(o) != null;
  425. }
  426. public void clear() {
  427. SortedHashMap.this.clear();
  428. }
  429. }
  430. public Collection<V> values() {
  431. Collection<V> vs = values;
  432. return (vs != null ? vs : (values = new Values()));
  433. }
  434. private class Values extends AbstractCollection<V> {
  435. public Iterator<V> iterator() {
  436. return newValueIterator();
  437. }
  438. public int size() {
  439. return size;
  440. }
  441. public boolean contains(Object o) {
  442. return containsValue(o);
  443. }
  444. public void clear() {
  445. SortedHashMap.this.clear();
  446. }
  447. }
  448. public Set<Map.Entry<K, V>> entrySet() {
  449. Set<Map.Entry<K, V>> es = entrySet;
  450. return (es != null ? es : (entrySet = (Set<Map.Entry<K, V>>) new EntrySet()));
  451. }
  452. private class EntrySet extends AbstractSet {
  453. public Iterator iterator() {
  454. return newEntryIterator();
  455. }
  456. public boolean contains(Object o) {
  457. if (!(o instanceof Map.Entry))
  458. return false;
  459. Map.Entry<K, V> e = (Map.Entry<K, V>) o;
  460. Entry<K, V> candidate = getEntry(e.getKey());
  461. return candidate != null && candidate.equals(e);
  462. }
  463. public boolean remove(Object o) {
  464. return removeMapping(o) != null;
  465. }
  466. public int size() {
  467. return size;
  468. }
  469. public void clear() {
  470. SortedHashMap.this.clear();
  471. }
  472. }
  473. }