java 数据结构(1)————HashMap


HashMap算是Java集合框架中使用最普遍的,对于他的基本特性和使用方法都知道了解,但是他的底层实现却很少去了解,也很少关注。最近空闲在网上找了很多关于这方面的帖子和博客,综合总结一下。

https://blog.csdn.net/UzV80PX5V412NE/article/details/78591134

https://www.cnblogs.com/chengxiao/p/6059914.html

一. HashMap的基本概念与结构

HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做entry。而HashMap的基本结构是

数组+单链表。基本特性 : Key,value可以为null , Key不可以重复,重复的话后面的value会覆盖前面的value,非线程安全。

	static class Entry<K,V> implements Map.Entry<K,V> {
		final K key; V value; Entry<K,V> next;//存储指向下一个Entry的引用,单链表结构 
		int hash;//对key的hashcode值进行hash运算后得到的值,存储在Entry,避免重复计算 
		/** * Creates new entry. */ 
		Entry(int h, K k, V v, Entry<K,V> n) { 
			value = v; 
			next = n; 
			key = k; 
			hash = h; 
		}
	}

Entry结构:       hash是首地址,根据key的值通过哈希算法(调用更底层语言)得出 hashcode()

    

public class HashMap<k,v>  
extends AbstractMap<k,v>  
implements Map<k,v>, Cloneable, Serializable  
{  
    transient Entry<k,v>[] table;//一个Entry数组  
    public V put(K key, V value) {  
        if (key == null)  
            return putForNullKey(value);  
        int hash = hash(key);  
        int i = indexFor(hash, table.length);  
        //遍历当前下标的Entry对象链,如果key已存在则替换  
        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))) {  
            V oldValue = e.value;  
            e.value = value;  
            e.recordAccess(this);  
            return oldValue;  
        }  
    }  
       addEntry(hash, key, value, i);  
        return null;  
    }  
}  

HashMap数组每一个元素的初始值都是Null。     

二 .HashMap常用方法

Put()方法:

HashMap数组的每一个元素不止是一个Entry对象,也是一个链表的头节点。每一个Entry对象通过Next指针指向它的下一个Entry节点。当新来的Entry映射到数组的位置有值时,只需要插入到数组对应的位置下的链表即可:

需要注意的是,新来的Entry节点插入链表时,使用的是“头插法”。因为默认后来的会先使用,或者被使用的概率更高。  

Get()方法

和put方法差不多,根据key,计算出hash值,然后再循环链表,顺序查找链表的key,是否符合。最后查出对应的value。

1、获取key 


2、通过hash函数得到hash值 
int hash=key.hashCode();

3、得到桶号(一般都为hash值对桶数求模) 
int index =hash%Entry[].length;

4、比较桶的内部元素是否与key相等,若都不相等,则没有找到。

5、取出相等的记录的value。



三 .HashMap的构造

HashMap有几个默认参数

//实际存储的key-value键值对的个数
transient int size;
//阈值,当table == {}时,该值为初始容量(初始容量默认为16);当table被填充了,也就是为table分配内存空间后,threshold一般为 capacity*loadFactory。HashMap在进行扩容时需要参考threshold,后面会详细谈到
int threshold;
//负载因子,代表了table的填充度有多少,默认是0.75
final float loadFactor;
//用于快速失败,由于HashMap非线程安全,在对HashMap进行迭代时,如果期间其他线程的参与导致HashMap的结构发生变化了(比如put,remove等操作),需要抛出异常ConcurrentModificationException
transient int modCount;

长度默认是16

index =  HashCode(Key) &  (Length - 1) 

哈希算法是将key的hashcode 与 数组长度-1做位运算,这样可以减少碰撞发生的概率(hash值相同)

HashMap中的碰撞探测(collision detection)以及碰撞的解决方法

当两个对象的hashcode相同时,它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用LinkedList存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在LinkedList中。这两个对象就算hashcode相同,但是它们可能并不相等。 那如何获取这两个对象的值呢?当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,遍历LinkedList直到找到值对象。找到bucket位置之后,会调用keys.equals()方法去找到LinkedList中正确的节点,最终找到要找的值对象使用不可变的、声明作final的对象,并且采用合适的equals()和hashCode()方法的话,将会减少碰撞的发生,提高效率。不可变性使得能够缓存不同键的hashcode,这将提高整个获取对象的速度,使用String,Interger这样的wrapper类作为键是非常好的选择。

重写equals()和hashcode()

在重写equals的方法的时候,必须注意重写hashCode方法,同时还要保证通过equals判断相等的两个对象,调用hashCode方法要返回同样的整数值。而如果equals判断不相等的两个对象,其hashCode可以相同

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值