前言

参考SpringSide-Util中提到的特殊Map,在特殊场景中性能较HashMap更优。IntObjectHashMap移植自Netty的工具类中,旨在优化性能。
IntObjectHashMap采用Int类型的开放地址而非使用链表来设计Key, 减少内存占用. 在数组中使用线性探测来解决Hash冲突。删除节点时需要压缩数组,
因此时间消耗解决O(N), 因此推荐使用偏小的负载因子(默认0.5)

源码解读

接口设计

  1. public interface IntObjectMap<V> extends Map<Integer, V> {
  2. /**
  3. * 基础Int类型的Entry
  4. */
  5. interface PrimitiveEntry<V> {
  6. int key();
  7. V value();
  8. void setValue(V value);
  9. }
  10. }
  11. V get(int key);
  12. V put(int key, V value);
  13. V remove(int key);
  14. Iterable<PrimitiveEntry<V>> entries();
  15. boolean containsKey(int key);

IntObjectHashMap源码解释

  1. public class IntObjectHashMap<V> implements IntObjectMap<V> {
  2. 默认初始容量为8
  3. public static final int DEFAULT_CAPACITY = 8;
  4. 默认加载因子
  5. public static final float DEFAULT_LOAD_FACTOR = 0.5f;
  6. null值的占位符,避免在数组中存储null.
  7. private static final Object NULL_VALUE = new Object();
  8. 最大的容量
  9. private int maxSize;
  10. 加载因子
  11. private final float loadFactor;
  12. Key数组
  13. private int[] keys;
  14. Value数组
  15. private V[] values;
  16. 实际元素数量,Map的大小
  17. private int size;
  18. 掩码,大小为数组的length-1, 用以与hash值作与运算, 比直接取模性能更加
  19. private int mask;
  20. Key视图
  21. private final Set<Integer> keySet = new KeySet();
  22. Entry视图
  23. private final Set<Entry<Integer, V>> entrySet = new EntrySet();
  24. 当前Map迭代器
  25. private final Iterable<PrimitiveEntry<V>> entries = new Iterable<PrimitiveEntry<V>>() {
  26. @Override
  27. public Iterator<PrimitiveEntry<V>> iterator() {
  28. return new PrimitiveIterator();
  29. }
  30. };
  31. 默认构造方法
  32. public IntObjectHashMap() {
  33. this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
  34. }
  35. public IntObjectHashMap(int initialCapacity) {
  36. this(initialCapacity, DEFAULT_LOAD_FACTOR);
  37. }
  38. public IntObjectHashMap(int initialCapacity, float loadFactor) {
  39. if (loadFactor <= 0.0f || loadFactor > 1.0f) {
  40. // Cannot exceed 1 because we can never store more than capacity elements;
  41. // using a bigger loadFactor would trigger rehashing before the desired load is reached.
  42. throw new IllegalArgumentException("loadFactor must be > 0 and <= 1");
  43. }
  44. this.loadFactor = loadFactor;
  45. 调整容量为2N次方
  46. int capacity = MathUtil.nextPowerOfTwo(initialCapacity);
  47. mask = capacity - 1;
  48. 分配Key数组,Value数组
  49. keys = new int[capacity];
  50. @SuppressWarnings({ "unchecked" })
  51. V[] temp = (V[]) new Object[capacity];
  52. values = temp;
  53. 实际的容量为数组最大的容量 * 负载因子
  54. maxSize = calcMaxSize(capacity);
  55. }
  56. 把取出的数据对外作空转换
  57. private static <T> T toExternal(T value) {
  58. return value == NULL_VALUE ? null : value;
  59. }
  60. 如果写入null,对内转换为NULL_VALUE存储
  61. @SuppressWarnings("unchecked")
  62. private static <T> T toInternal(T value) {
  63. return value == null ? (T) NULL_VALUE : value;
  64. }
  65. @Override
  66. public V get(int key) {
  67. 根据当前key找到对应的索引,如果有冲突,则继续找下一个, 如果冲突的key对应的索引中的实际Key相同,则查找成功
  68. int index = indexOf(key);
  69. return index == -1 ? null : toExternal(values[index]);
  70. }
  71. @Override
  72. public V put(int key, V value) {
  73. int startIndex = hashIndex(key);
  74. int index = startIndex;
  75. for (;;) {
  76. 未冲突,写入当前kv
  77. if (values[index] == null) {
  78. // Found empty slot, use it.
  79. keys[index] = key;
  80. values[index] = toInternal(value);
  81. growSize();
  82. return null;
  83. }
  84. 当前key已存在,用新value覆盖原值
  85. if (keys[index] == key) {
  86. // Found existing entry with this key, just replace the value.
  87. V previousValue = values[index];
  88. values[index] = toInternal(value);
  89. return toExternal(previousValue);
  90. }
  91. 继续找下个非空的value索引
  92. if ((index = probeNext(index)) == startIndex) {
  93. // Can only happen if the map was full at MAX_ARRAY_SIZE and couldn't grow.
  94. throw new IllegalStateException("Unable to insert");
  95. }
  96. }
  97. }
  98. @Override
  99. public void putAll(Map<? extends Integer, ? extends V> sourceMap) {
  100. if (sourceMap instanceof IntObjectHashMap) {
  101. 优化版本
  102. @SuppressWarnings("unchecked")
  103. IntObjectHashMap<V> source = (IntObjectHashMap<V>) sourceMap;
  104. for (int i = 0; i < source.values.length; ++i) {
  105. V sourceValue = source.values[i];
  106. if (sourceValue != null) {
  107. 逐个put
  108. put(source.keys[i], sourceValue);
  109. }
  110. }
  111. return;
  112. }
  113. 逐个添加Entry
  114. for (Entry<? extends Integer, ? extends V> entry : sourceMap.entrySet()) {
  115. put(entry.getKey(), entry.getValue());
  116. }
  117. }
  118. 1. 如果key对应value不存在,返回null
  119. 2. 找到对应的索引
  120. @Override
  121. public V remove(int key) {
  122. int index = indexOf(key);
  123. if (index == -1) {
  124. return null;
  125. }
  126. 找到目标值
  127. V prev = values[index];
  128. removeAt(index);
  129. return toExternal(prev);
  130. }
  131. @Override
  132. public int size() {
  133. return size;
  134. }
  135. @Override
  136. public boolean isEmpty() {
  137. return size == 0;
  138. }
  139. 数组清0
  140. @Override
  141. public void clear() {
  142. Arrays.fill(keys, 0);
  143. Arrays.fill(values, null);
  144. size = 0;
  145. }
  146. @Override
  147. public boolean containsKey(int key) {
  148. return indexOf(key) >= 0;
  149. }
  150. @Override
  151. public boolean containsValue(Object value) {
  152. @SuppressWarnings("unchecked")
  153. V v1 = toInternal((V) value);
  154. for (V v2 : values) {
  155. // The map supports null values; this will be matched as NULL_VALUE.equals(NULL_VALUE).
  156. if (v2 != null && v2.equals(v1)) {
  157. return true;
  158. }
  159. }
  160. return false;
  161. }
  162. @Override
  163. public Iterable<PrimitiveEntry<V>> entries() {
  164. return entries;
  165. }
  166. @Override
  167. public Collection<V> values() {
  168. return new AbstractCollection<V>() {
  169. @Override
  170. public Iterator<V> iterator() {
  171. return new Iterator<V>() {
  172. final PrimitiveIterator iter = new PrimitiveIterator();
  173. @Override
  174. public boolean hasNext() {
  175. return iter.hasNext();
  176. }
  177. @Override
  178. public V next() {
  179. return iter.next().value();
  180. }
  181. @Override
  182. public void remove() {
  183. throw new UnsupportedOperationException();
  184. }
  185. };
  186. }
  187. @Override
  188. public int size() {
  189. return size;
  190. }
  191. };
  192. }
  193. @Override
  194. public int hashCode() {
  195. // Hashcode is based on all non-zero, valid keys. We have to scan the whole keys
  196. // array, which may have different lengths for two maps of same size(), so the
  197. // capacity cannot be used as input for hashing but the size can.
  198. int hash = size;
  199. for (int key : keys) {
  200. // 0 can be a valid key or unused slot, but won't impact the hashcode in either case.
  201. // This way we can use a cheap loop without conditionals, or hard-to-unroll operations,
  202. // or the devastatingly bad memory locality of visiting value objects.
  203. // Also, it's important to use a hash function that does not depend on the ordering
  204. // of terms, only their values; since the map is an unordered collection and
  205. // entries can end up in different positions in different maps that have the same
  206. // elements, but with different history of puts/removes, due to conflicts.
  207. hash ^= hashCode(key);
  208. }
  209. return hash;
  210. }
  211. @Override
  212. public boolean equals(Object obj) {
  213. if (this == obj) {
  214. return true;
  215. }
  216. if (!(obj instanceof IntObjectMap)) {
  217. return false;
  218. }
  219. @SuppressWarnings("rawtypes")
  220. IntObjectMap other = (IntObjectMap) obj;
  221. if (size != other.size()) {
  222. return false;
  223. }
  224. for (int i = 0; i < values.length; ++i) {
  225. V value = values[i];
  226. if (value != null) {
  227. int key = keys[i];
  228. Object otherValue = other.get(key);
  229. if (value == NULL_VALUE) {
  230. if (otherValue != null) {
  231. return false;
  232. }
  233. } else if (!value.equals(otherValue)) {
  234. return false;
  235. }
  236. }
  237. }
  238. return true;
  239. }
  240. @Override
  241. public boolean containsKey(Object key) {
  242. return containsKey(objectToKey(key));
  243. }
  244. @Override
  245. public V get(Object key) {
  246. return get(objectToKey(key));
  247. }
  248. @Override
  249. public V put(Integer key, V value) {
  250. return put(objectToKey(key), value);
  251. }
  252. @Override
  253. public V remove(Object key) {
  254. return remove(objectToKey(key));
  255. }
  256. @Override
  257. public Set<Integer> keySet() {
  258. return keySet;
  259. }
  260. @Override
  261. public Set<Entry<Integer, V>> entrySet() {
  262. return entrySet;
  263. }
  264. private int objectToKey(Object key) {
  265. return ((Integer) key).intValue();
  266. }
  267. 根据Key定位具体的索引, 采用双Hash来探测,返回Key对应的下标索引,如果当前Keys中还没有Key,则返回-1
  268. private int indexOf(int key) {
  269. 默认情况下的初始索引
  270. int startIndex = hashIndex(key);
  271. int index = startIndex;
  272. for (;;) {
  273. if (values[index] == null) {
  274. 当前Key对应的索引,其关联的Value为空,说明此位置未产生冲突,返回-1.表示此索引可用
  275. return -1;
  276. }
  277. 当前key已经存在,返回对应的索引
  278. if (key == keys[index]) {
  279. return index;
  280. }
  281. key冲突,根据当前Key的索引找到下一个非空的索引
  282. if ((index = probeNext(index)) == startIndex) {
  283. return -1;
  284. }
  285. }
  286. }
  287. 根据Key计算Key在数组的下标
  288. private int hashIndex(int key) {
  289. // The array lengths are always a power of two, so we can use a bitmask to stay inside the array bounds.
  290. return hashCode(key) & mask;
  291. }
  292. int类型的KeyHash值为原生值
  293. private static int hashCode(int key) {
  294. return key;
  295. }
  296. 探测下一个索引位置,
  297. private int probeNext(int index) {
  298. // The array lengths are always a power of two, so we can use a bitmask to stay inside the array bounds.
  299. return (index + 1) & mask;
  300. }
  301. 新增Key-Value后,递增Size.如果超过阈值,做扩容
  302. private void growSize() {
  303. size++;
  304. if (size > maxSize) {
  305. if (keys.length == Integer.MAX_VALUE) {
  306. throw new IllegalStateException("Max capacity reached at size=" + size);
  307. }
  308. 基于当前数组大小的2
  309. rehash(keys.length << 1);
  310. }
  311. }
  312. 删除索引对应的Key,Value, 如果存在冲突,则打破冲突位置
  313. private boolean removeAt(final int index) {
  314. --size;
  315. 建议清除key
  316. keys[index] = 0;
  317. values[index] = null;
  318. boolean movedBack = false;
  319. int nextFree = index;
  320. for (int i = probeNext(index); values[i] != null; i = probeNext(i)) {
  321. int bucket = hashIndex(keys[i]);
  322. 当前的删除的index右边还存在冲突的Key, Key需要向左移动.目的是降低冲突的位置
  323. if (i < bucket && (bucket <= nextFree || nextFree <= i) || bucket <= nextFree && nextFree <= i) {
  324. // Move the displaced entry "back" to the first available position.
  325. keys[nextFree] = keys[i];
  326. values[nextFree] = values[i];
  327. movedBack = true;
  328. // Put the first entry after the displaced entry
  329. keys[i] = 0;
  330. values[i] = null;
  331. nextFree = i;
  332. }
  333. }
  334. return movedBack;
  335. }
  336. rehash之前计算最大的数组实际容量
  337. private int calcMaxSize(int capacity) {
  338. 调整一下大小,以避免loadFactor1,保证还有可用的槽
  339. int upperBound = capacity - 1;
  340. return Math.min(upperBound, (int) (capacity * loadFactor));
  341. }
  342. rehash数组,转移数据,扩容或缩容
  343. private void rehash(int newCapacity) {
  344. int[] oldKeys = keys;
  345. V[] oldVals = values;
  346. keys = new int[newCapacity];
  347. @SuppressWarnings({ "unchecked" })
  348. V[] temp = (V[]) new Object[newCapacity];
  349. values = temp;
  350. maxSize = calcMaxSize(newCapacity);
  351. mask = newCapacity - 1;
  352. for (int i = 0; i < oldVals.length; ++i) {
  353. V oldVal = oldVals[i];
  354. if (oldVal != null) {
  355. // Inlined put(), but much simpler: we don't need to worry about
  356. // duplicated keys, growing/rehashing, or failing to insert.
  357. 内联put方法的逻辑并作冲突检测
  358. int oldKey = oldKeys[i];
  359. int index = hashIndex(oldKey);
  360. for (;;) {
  361. if (values[index] == null) {
  362. keys[index] = oldKey;
  363. values[index] = oldVal;
  364. break;
  365. }
  366. 在新数组中存在冲突, 寻址下一个位置
  367. // Conflict, keep probing. Can wrap around, but never reaches startIndex again.
  368. index = probeNext(index);
  369. }
  370. }
  371. }
  372. }
  373. @Override
  374. public String toString() {
  375. if (isEmpty()) {
  376. return "{}";
  377. }
  378. StringBuilder sb = new StringBuilder(4 * size);
  379. sb.append('{');
  380. boolean first = true;
  381. for (int i = 0; i < values.length; ++i) {
  382. V value = values[i];
  383. if (value != null) {
  384. if (!first) {
  385. sb.append(", ");
  386. }
  387. sb.append(keyToString(keys[i])).append('=').append(value == this ? "(this Map)" : toExternal(value));
  388. first = false;
  389. }
  390. }
  391. return sb.append('}').toString();
  392. }
  393. /**
  394. * Helper method called by {@link #toString()} in order to convert a single map key into a string. This is protected
  395. * to allow subclasses to override the appearance of a given key.
  396. */
  397. protected String keyToString(int key) {
  398. return Integer.toString(key);
  399. }
  400. /**
  401. * Set implementation for iterating over the entries of the map.
  402. */
  403. private final class EntrySet extends AbstractSet<Entry<Integer, V>> {
  404. @Override
  405. public Iterator<Entry<Integer, V>> iterator() {
  406. return new MapIterator();
  407. }
  408. @Override
  409. public int size() {
  410. return IntObjectHashMap.this.size();
  411. }
  412. }
  413. /**
  414. * Set implementation for iterating over the keys.
  415. */
  416. private final class KeySet extends AbstractSet<Integer> {
  417. @Override
  418. public int size() {
  419. return IntObjectHashMap.this.size();
  420. }
  421. @Override
  422. public boolean contains(Object o) {
  423. return IntObjectHashMap.this.containsKey(o);
  424. }
  425. @Override
  426. public boolean remove(Object o) {
  427. return IntObjectHashMap.this.remove(o) != null;
  428. }
  429. @Override
  430. public boolean retainAll(Collection<?> retainedKeys) {
  431. boolean changed = false;
  432. for (Iterator<PrimitiveEntry<V>> iter = entries().iterator(); iter.hasNext();) {
  433. PrimitiveEntry<V> entry = iter.next();
  434. if (!retainedKeys.contains(entry.key())) {
  435. changed = true;
  436. iter.remove();
  437. }
  438. }
  439. return changed;
  440. }
  441. @Override
  442. public void clear() {
  443. IntObjectHashMap.this.clear();
  444. }
  445. @Override
  446. public Iterator<Integer> iterator() {
  447. return new Iterator<Integer>() {
  448. private final Iterator<Entry<Integer, V>> iter = entrySet.iterator();
  449. @Override
  450. public boolean hasNext() {
  451. return iter.hasNext();
  452. }
  453. @Override
  454. public Integer next() {
  455. return iter.next().getKey();
  456. }
  457. @Override
  458. public void remove() {
  459. iter.remove();
  460. }
  461. };
  462. }
  463. }
  464. /**
  465. * Iterator over primitive entries. Entry key/values are overwritten by each call to {@link #next()}.
  466. */
  467. private final class PrimitiveIterator implements Iterator<PrimitiveEntry<V>>, PrimitiveEntry<V> {
  468. private int prevIndex = -1;
  469. private int nextIndex = -1;
  470. private int entryIndex = -1;
  471. private void scanNext() {
  472. for (;;) {
  473. if (++nextIndex == values.length || values[nextIndex] != null) {
  474. break;
  475. }
  476. }
  477. }
  478. @Override
  479. public boolean hasNext() {
  480. if (nextIndex == -1) {
  481. scanNext();
  482. }
  483. return nextIndex < keys.length;
  484. }
  485. @Override
  486. public PrimitiveEntry<V> next() {
  487. if (!hasNext()) {
  488. throw new NoSuchElementException();
  489. }
  490. prevIndex = nextIndex;
  491. scanNext();
  492. // Always return the same Entry object, just change its index each time.
  493. entryIndex = prevIndex;
  494. return this;
  495. }
  496. @Override
  497. public void remove() {
  498. if (prevIndex < 0) {
  499. throw new IllegalStateException("next must be called before each remove.");
  500. }
  501. if (removeAt(prevIndex)) {
  502. // removeAt may move elements "back" in the array if they have been displaced because their spot in the
  503. // array was occupied when they were inserted. If this occurs then the nextIndex is now invalid and
  504. // should instead point to the prevIndex which now holds an element which was "moved back".
  505. nextIndex = prevIndex;
  506. }
  507. prevIndex = -1;
  508. }
  509. // Entry implementation. Since this implementation uses a single Entry, we coalesce that
  510. // into the Iterator object (potentially making loop optimization much easier).
  511. @Override
  512. public int key() {
  513. return keys[entryIndex];
  514. }
  515. @Override
  516. public V value() {
  517. return toExternal(values[entryIndex]);
  518. }
  519. @Override
  520. public void setValue(V value) {
  521. values[entryIndex] = toInternal(value);
  522. }
  523. }
  524. /**
  525. * Iterator used by the {@link Map} interface.
  526. */
  527. private final class MapIterator implements Iterator<Entry<Integer, V>> {
  528. private final PrimitiveIterator iter = new PrimitiveIterator();
  529. @Override
  530. public boolean hasNext() {
  531. return iter.hasNext();
  532. }
  533. @Override
  534. public Entry<Integer, V> next() {
  535. if (!hasNext()) {
  536. throw new NoSuchElementException();
  537. }
  538. iter.next();
  539. return new MapEntry(iter.entryIndex);
  540. }
  541. @Override
  542. public void remove() {
  543. iter.remove();
  544. }
  545. }
  546. /**
  547. * A single entry in the map.
  548. */
  549. final class MapEntry implements Entry<Integer, V> {
  550. private final int entryIndex;
  551. MapEntry(int entryIndex) {
  552. this.entryIndex = entryIndex;
  553. }
  554. @Override
  555. public Integer getKey() {
  556. verifyExists();
  557. return keys[entryIndex];
  558. }
  559. @Override
  560. public V getValue() {
  561. verifyExists();
  562. return toExternal(values[entryIndex]);
  563. }
  564. @Override
  565. public V setValue(V value) {
  566. verifyExists();
  567. V prevValue = toExternal(values[entryIndex]);
  568. values[entryIndex] = toInternal(value);
  569. return prevValue;
  570. }
  571. private void verifyExists() {
  572. if (values[entryIndex] == null) {
  573. throw new IllegalStateException("The map entry has been removed");
  574. }
  575. }
  576. }
  577. }