JDK1.7之HashMap源码学习

前言:第一次写学习贴,看了很多的帖子,然后也去看了hashMap的源码,想尝试自己去写一下自己对于这个东西的理解,并不一定是对的,可能也就是写出来让自己加深一下基础印象,如果有些的不好的地方请指出。

此博客应该是以JDK1.7为基础的,但是我是以1.8来说的不要混淆了(只因为我懒得打开1.8的源码

1.HashMap是什么:让我们重新认识一下这个小哥儿

HashMap是java中AbstractMap的子类,是键值对集合java.util.Map的实现类,是作为Map家族中被使用频率最高的小哥哥

HashMap是java面试时基础中被询问频率极高的你不得不懂得面试题1.2.3

2.HashMap基础了解:HashMap家中拥有着好几个兄弟姐妹,但是咱们只是来了解它的,就不多介绍了,先说的感觉最基础的

HashMap是一个无序的键值对集合,是以Key-Value的形式存储数据的,它只允许一个Key为null值,允许多个value为null值

HashMap是一个非线程安全的键值对集合,即当你使用多线程去操作它时,他可能会出现数据不一致,但是如果你需要它达到线程安全的条件时他也是可以做到的,可以用 Collections(肯来克辛斯)的synchronizedMap(神奎耐日德Map)方法使HashMap具有线程安全的能力,或是选择使用ConcurrentHashMap(肯卡韧特HashMap)这样一个线程安全的Map集合达到目的。

最开始HashMap底层是由数组加链表来实现的,但是在JDK1.8以后在原有的基础数引入了红黑树这样一个二叉树结构的树来优化HashMap

3.让我们去源码层参观一下HashMap

HashMap的默认长度

/*** The default initial capacity - MUST be a power of two.*/
static final int DEFAULT_INITIAL_CAPACITY = 16;

HashMap的默认负载因子

/*** The load factor used when none specified in constructor.*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;

HashMap的最大长度

/*** The maximum capacity, used if a higher value is implicitly specified* by either of the constructors with arguments.* MUST be a power of two <= 1<<30.*/
static final int MAXIMUM_CAPACITY = 1 << 30;

HashMap中修改map长度和负载因子的构造函数(中文注释是我自己加的,个人理解)

--initialCapacity:长度

--loadFactor :负载因子

public HashMap(int initialCapacity, float loadFactor) {/*如果初始化的长度小于0则抛出异常*/if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);/*如果初始化的长度大于最大值常量那么就以最大值常量为默认长度*/if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;/*如果初始化负载因子小于等于0或则不是一个数字那么抛出异常*/if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);// Find a power of 2 >= initialCapacity/*这里的算法我开始没看懂,然后我把代码拿出来测试了一下,我觉得应该是求比你初始化长度更大的一个2的平方根的值比如我initialCapacity初始化值是15那么capacity的值就是16,如果我的值>16且小于16X2 那么他的值就一定是32(推测的,未经       测试)之所以这么做这么个算法的原因是因为它本身对于Hash冲突的算法的原因,如果后面有聊到这个我们在解释它本身的hash算法*/int capacity = 1;while (capacity < initialCapacity)capacity <<= 1;/*赋值这个就不需要我多解释了吧*/this.loadFactor = loadFactor;/*我觉得这里应该是求一个阈值,就是达到这个值以后map就会进行扩容,threshold是一个全局的变量*/threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);/*看到new关键字就不提了吧,初始化一个Entry的素组长度为capacity*/table = new Entry[capacity];useAltHashing = sun.misc.VM.isBooted() &&(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);init();
}

到此这个构造函数就结束了,去看看其他的

发现一个get方法,让我们来看看

public V get(Object key) {/*这行代码证明HashMap是可以有null的Key的*/if (key == null)return getForNullKey();/*对象获取,源码往下*/Entry entry = getEntry(key);/*这个对象不为空就返回这个对象得value值*/return null == entry ? null : entry.getValue();
}

--获取HashMap中key值为空的value源码

private V getForNullKey() {/*循环取HashMap*/for (Entry e = table[0]; e != null; e = e.next) {/*如果这个key值==null就返回这个key值得value值,最后则返回null*/if (e.key == null)return e.value;}return null;
}

--获取HashMap中key值对应得Value源码

final Entry getEntry(Object key) {/*运用三目表达式获取到这个key值得hash码*/int hash = (key == null) ? 0 : hash(key);/*根据hash码进行运算得到key值对应的对象在数组中的坐标,最后返回对象*/for (Entry e = table[indexFor(hash, table.length)];e != null;e = e.next) {Object k;if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;}return null;
}

--hash码获取的源码(这里的算法对我来说太高级了不解释)

final int hash(Object k) {int h = 0;if (useAltHashing) {if (k instanceof String) {return sun.misc.Hashing.stringHash32((String) k);}h = hashSeed;}h ^= k.hashCode();// This function ensures that hashCodes that differ only by// constant multiples at each bit position have a bounded// number of collisions (approximately 8 at default load factor).h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);
}

再看下put方法

public V put(K key, V value) {/*同理于上面得get空值的key,HashMap是允许一个主键为空的*/if (key == null)return putForNullKey(value);/*获取hash码*/int hash = hash(key);/*通过算法获取坐标*/int i = indexFor(hash, table.length);/*这个Entry我觉得有必要解释一波,这是一个实现了Map.Entrty的链表结构对象,上文也说了Hash底层是数组+链表+红黑树实现的(1.8                   才有的红黑树,我这里是1.7的源码)*/for (Entry e = table[i]; e != null; e = e.next) {Object k;/*如果put的key值存在*/if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {/*进行覆盖,然后返回旧的Value*/V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}/*如果key值不存在走这里*/modCount++;addEntry(hash, key, value, i);return null;
}

--源码addEntry

void addEntry(int hash, K key, V value, int bucketIndex) {/*判断是否需要扩容*/if ((size >= threshold) && (null != table[bucketIndex])) {/*每次扩容都会是之前长度X2*/resize(2 * table.length);/*获取key值hash码*/hash = (null != key) ? hash(key) : 0;/*对hash码进行算法运算以后得到坐标*/bucketIndex = indexFor(hash, table.length);}/*新增*/createEntry(hash, key, value, bucketIndex);
}

  /*这里的代码就是一个新的对象添加到了HashMap的数组中,然后数组长度++*/

void createEntry(int hash, K key, V value, int bucketIndex) {Entry e = table[bucketIndex];table[bucketIndex] = new Entry<>(hash, key, value, e);size++;
}

今天就到这里吧,有什么问题欢迎指出

外贴上我看的两篇对于HashMap写的比较好的(1.8)帖子以供大家共同学习

Java8系列,重新认识HashMap

一个HashMap如何面试聊上半小时

大家回见

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部