
细读源码是《马士兵教育》旗下赏析源码的栏目。我们赏析源码的目的不是为了炫技,而是为了去理解作者的设计思想,并取其精华,去其糟粕,从而写出更加优秀的代码。另一方面,也可以给面试加分。代码好的坏的评价,不可避免地会代入个人的主观色彩,大家和而不同。
由于HashMap在日常工作或者的面试中,
遇到的频率非常高,
所以今天就赏析一下HashMap的源码。
本文将从以下三个部分进行讲解:
1、赏析代码中实现优秀,精妙,值得我们借鉴的地方;
2、分析代码实现不好的地方及其原因,以及如何通过重构来解决问题;
3、哪些HashMap的小技巧,可以来提高程序的执行效率;

一.代码中设计好的地方
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}
public static void main(String[] args) { for (int i = 0; i < 1024; i++) { Float f = (float) i; System.out.println(f.hashCode() % 2048); }}
static final int tableSizeFor(int cap) { if (cap <= 1) { return 1; } if (cap >= MAXIMUM_CAPACITY) { return MAXIMUM_CAPACITY; } int ret = 2; while (ret < cap) { ret = ret << 1; } return ret; }
static final int tableSizeFor(int cap) {int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;}
4.扩容resize细节的处理
JKD1.8对resize的过程进行了优化,我们摘取部分代码进行分析,代码如下:
final Node[] resize() { NodeloHead = null, loTail = null; NodehiHead = null, hiTail = null; Nodenext; e = table[j]; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; }}
二.代码中需要改进的地方
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node[] tab; Nodep; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; }
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node[] tab = table; int n = tab == null ? 0 : tab.length; if (n == 0) { tab = resize(); n = tab.length; }}
public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity);}
一般情况下,我们在程序执行时间,使用空间和可读性,可维护性做选择的时候,一般选择可读性,除非是特殊的场景。而HashMap明显是选择了节省空间这一策略,也降低了代码的可读性和可维护性。
final Node[] resize() { Node[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float) newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ? (int) ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes", "unchecked"}) Node[] newTab = (Node[]) new Node[newCap]; table = newTab; if (oldTab != null) { //…… //…… } return newTab;}
重构后的结果:
final Node[] initOrResize() {if (table == null) {return doInit();}return doResize();}private Node[] doInit() {int initCapacity = threshold == 0 ? DEFAULT_INITIAL_CAPACITY : threshold;float ft = (float) initCapacity * loadFactor;threshold = (initCapacity < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ?(int) ft : Integer.MAX_VALUE);table = (Node[]) new Node[initCapacity]; return table;}private Node[] doResize() {if (table.length >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return table;}Node[] oldTab = table; int oldCap = oldTab.length;int newCap = oldCap << 1;if (newCap < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) {threshold = threshold << 1; // double threshold} else {float ft = (float) newCap * loadFactor;threshold = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ?(int) ft : Integer.MAX_VALUE);}Node[] newTab = (Node []) new Node[newCap]; table = newTab;//……//……return newTab;}
三.使用HashMap的小技巧
public class HashMapCapacityTest {private static final int SIZE = 10000000;private static final float DEFAULT_LOAD_FACTOR = 0.75f;public static void main(String[] args) {long totalA = 0, totalB = 0;for (int i = 0; i < 1000; i++) {totalA += testHashMapUseDefaultCapacity();totalB += testHashMapAssignedCapacity();System.out.println(totalA + " " + totalB + " " + (totalA * 1.0 totalB));}}private static long testHashMapUseDefaultCapacity() {long start = System.currentTimeMillis();Map map = new HashMap();for (int i = 0; i < SIZE; i++) {map.put(i, i);}return System.currentTimeMillis() - start;}private static long testHashMapAssignedCapacity() {long start = System.currentTimeMillis();int capacity = (int) (SIZE DEFAULT_LOAD_FACTOR);Map map = new HashMap(capacity);for (int i = 0; i < SIZE; i++) {map.put(i, i);}return System.currentTimeMillis() - start;}}
public class HashMapGetTest {public static void main(String[] args) {Map map = new HashMap(); map.put("1", 1);System.out.println(getIntValueFromMap(map, "2"));}private static int getIntValueFromMap(Mapmap, String key) {return map.get(key);}}
public class HashMapGetTest {public static void main(String[] args) {Map map = new HashMap(); map.put("1", 1);System.out.println(getIntValueFromMap(map, "2"));}private static int getIntValueFromMap(Mapmap, String key) {if (map.containsKey(key)) {return map.get(key);}return 0;}}
public class HashMapGetTest {public static void main(String[] args) {Map map = new HashMap(); map.put("1", 1);System.out.println(getIntValueFromMap(map, "2"));}private static int getIntValueFromMap(Mapmap, String key ) {Integer value = map.get(key);if (value == null) {return 0;}return value.intValue();}}
第三版斧,HashMap使用小技巧。
欢迎大家的持续关注,会定期更新哒~
长按“识别二维码”点关注
看都看完了,还不点这里试试

文章转载自马士兵,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。





