• HashMap源码分析(jdk 7)


    HashMap源码分析(jdk 7)

    1.创建一个map对象

    HashMap map = new HashMap();	//底层创建了长度是16的一维数组Entry[] table
    

    底层实现HashMap.java

    transient Entry<K,V>[] table;			//Entry类型的底层数组table
    

    第一步:调用无参构造器

    public HashMap() {
            this(DEFAULT_INITIAL_CAPACITY,DEFAULT_LOAD_FACTOR);	//调用当前的重载构造器
        }
    

    DEFAULT_INITIAL_CAPACITY:默认初始容量为16。

    DEFAULT_LOAD_FACTOR:默认的加载因子为0.75。

    第二步:调用本类的重载构造器

    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);
            int capacity = 1;
        	while(capacity < initialCapacity)
                capacity <<= 1;				//capacity左移一位1,2,4,8,16,它决定了底层创建数组的长度,一定是2的多少次幂
        	this.loadFactor = loadFactor;
        	threshold = (int)Math.min(capacity * loadFactor,MAXIMUM_CAPACITY + 1);	//12 = 16 * 0.75 
        	table = new Entry[capacity];		//创建了底层长度为16的数组赋给table
        }
    
    • 注意HashMap map = new HashMap(15);底层并不一定是创建长度为15的数组

    • threshold:临界值12。它影响着扩容。当底层创建了长度为16的数组时,它并不是添加第17个时才进行扩容,原因是数组的元素有可能永远存不满(有些位置上的元素是以链表的形式存储的)。这里是否扩容要看threshold临界值,超过12就要进行扩容

    2.添加数据,put( )方法

    map.put(key1,value1);
    

    底层实现HashMap.java

    第一步:调用put()

    public V put(K key, V value) {
        if(key == null)						//这里说明了HashMap的key可以为空值
        	return putForNullKey(value);
        int hash = hash(key);				//计算哈希值,hash()中调用了hashCode()方法
        int i = indexFor(hash,table.length);	//此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置 
        
        for(Entry<K,V> e = table[i]; e != null; e = e.next){	
        /*拿出i位置上的元素,判空,e = e.next相当于将i位置上的链表走一遍*/
        	Object k;
        	if( e.hash == hash && ((e.key = key) == key) || key.equals(k))){//比较哈希值、key的地址、key所对应的数据
        		V oldValue = e.value;
        		e.value = value;	// 用value1替换已存在数据e的value
        		e.recordAccess(m:this);
        		return oldValue;
        	}
        }
        
        modCount++;
        addEntry(hash,key,value,i);
        return null; 
    }
    

    第一种情况i位置上的数据为空即e = null时,跳过for,执行addEntry(hash,key,value,i),数据key1-value1添加成功。

    • hash:当前添加的数据key1的哈希值
    • key:当前添加的数据key1
    • value:当前添加的数据value1
    • i:当前添加的数据在数组中存放的位置

    第二种情况i位置上的数据不为空,key1的哈希值和已经存在的数据的哈希值都不相同,跳出for,执行addEntry(hash,key,value,i),数据key1-value1添加成功。

    第三种情况i位置上的数据不为空,key1的哈希值和已经存在的数据的哈希值相同

    1. (e.key = key) == key) || key.equals(k)返回false,跳出false,执行addEntry(hash,key,value,i),数据key1-value1添加成功。

    2. (e.key = key) == key) || key.equals(k)返回true,执行e.value = value; 用value1替换已存在数据e的value。

    第二步:调用addEntry()

    void addEntry(int hash,K key,V value,int bucketIndex){
        if((size >= threshold) && (null != table[bucketIndex])){	//扩容的条件:超出临界值(且要存放的位置非空)时
            resize(newCapacity:2*table.length);					    //扩容为原来的2倍
            
            /*这里需要重新计算原来数组中数据在新的数组中的存放位置,可能原来在链表中,新数组中变为在数组上*/
            hash = (null != key)?hash(key):0;
            bucketIndex = indexFor(hash,table.length);
        }
        //不需要扩容时,执行createEntry()
        createEntry(hash,key,value,bucketIndex);
    }
    

    第三步:不需要扩容时,调用createEntry(),直接进行添加。

    void createEntry(int hash,K key,V value,int bucketIndex){
        Entry<K,V> e = table[bucketIndex];	//先将原来位置上的元素取出
        table[bucketIndex] = new Entry<>(hash,key,value,e);
        size++;
    }
    

    解释:table[bucketIndex] = new Entry<>(hash,key,value,e);将原来bucketIndex位置上的数据作为新添加数据的next出现,然后将新添加的数据key1-value1放在bucketIndex位置上。如图所示


    添加前                               添加后

    相信自己,你能行!!!
  • 相关阅读:
    asp.net中读取带有加号(+)的Cookie,会自动把加号替换为空格
    简单实现分行输出的javascript代码
    大学我们应该做什么
    近日个人要闻
    WPF学习笔记“路由事件”一:路由事件基础
    WPF学习笔记“路由事件”二:路由事件基础
    WPF学习笔记“命令”三:执行命令
    WPF学习笔记“命令”二:命令库
    WPF学习笔记“命令”五:自定义高级命令的使用
    WPF学习笔记“布局”一:基础
  • 原文地址:https://www.cnblogs.com/byd-hold-on/p/14051016.html
Copyright © 2020-2023  润新知