之前只会简单的应用,具体的原理不怎么懂,最近一直在看,总算懂了点,如今把个人感悟写出来,但愿能够帮到他人.java
首先,HashMap 是数组与链表的结合体.空构造的状况下,看下图:数组
这个 table[] 数组就是你用来存放的, 一个 Entry<K,V> 表明一组 key-value 组合.函数
看put方法的源码:性能
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value);//若是key为null,调用这个方法 int hash = hash(key);//计算key的hash值 int i = indexFor(hash, table.length);//根据hash值获得数组下标 //遍历开始 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {//Entry链表中,key值已经存在了 V oldValue = e.value; e.value = value;//新的value值覆盖旧的value e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i);//key不存在,须要新增一个 return null; }
看了源码就能知道,key-value中,起决定做用的是key,value就只有在存值的时候有点用,table下标和是否已经有这个key,都要靠key来肯定.this
先判断key是否为空,若是为空,执行putForNullKey.code
若是不为空,再计算key的hashCode肯定table数组的下标,获得Entry链表,接着开始遍历.如今就要用到 equals 方法了.若是hashCode相等,而且 ==成立 或者 equals成立,那就说明,这个key,之前已经存在了,须要用如今的value覆盖之前的value.ci
Entry链表的证实:get
因为本人不知道如何改写String类的 hashCode 方法和 equals 方法,没法放到同一个下标下,但我能够重写一个类,里面再重写这两个方法.以下:源码
public class Person { private String name; public Person(String name){ this.name=name; } /**重写 hashCode 方法**/ public int hashCode(){ return 1; } /**重写 equals 方法**/ public boolean equals(){ return false; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public static void main(String[] args) { Map<Person,Integer> map=new HashMap<Person,Integer>(); Person xiao=new Person("xiaofeiji"); Person da=new Person("dabaicai"); map.put(xiao, 1); map.put(da, 4); System.out.println(map.keySet()); }
因为hashCode方法返回值都是1,因此获得的数组下标同样,也就是说放在同一个链表中博客
把数组展开:
固然实际中是不可能这个写的,咱们要作的就是让数据分散开来,越均匀越好,要是存放在一个链表中,浪费空间,也严重影响查询性能.
以上是不须要扩容的状况下(数据少),若是数据量很大,数组长度仍是16,那样链表也会很长,也是很差的,这会就须要扩容了.即数组长度翻倍.
若是是空构造,在HashMap中,threshold=12(临界值),loadFactor=0.75(负载因子).
threshold=table数组长度 * loadFactor.一开始的table长度为16,因此临界值为12.这表明什么呢?表明着,若是放入12个key不一样的键值对,table数组是不会扩容的,当放入第13个,数组长度就会翻倍.
下面的构造函数中,直接给了 临界值与加载因子
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; threshold = initialCapacity; init(); }
写了这么点,感受不少懂的东西可是没法表达出来,真心佩服那些大牛.不但本身懂了,写出来的东西也能让别人懂.
但愿个人这篇博客能够给人帮助.若是哪里写的很差,请指点,谢谢.