java 中 HashMap 的理解

    之前只会简单的应用,具体的原理不怎么懂,最近一直在看,总算懂了点,如今把个人感悟写出来,但愿能够帮到他人.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();
    }

 

写了这么点,感受不少懂的东西可是没法表达出来,真心佩服那些大牛.不但本身懂了,写出来的东西也能让别人懂.

但愿个人这篇博客能够给人帮助.若是哪里写的很差,请指点,谢谢.

相关文章
相关标签/搜索