概念HasnMap是基于map接口实现的。元素以键值对的形式存储,键和值都可以为null,因为key不允许重复,所以只能有一个key为nullHaasnMap是无序不重复的,并且HashMap不是线程安全的。JDK7HashMap的数据结构是:数组+链表。JDK8HashMap的数据结构是:数组+链表+红黑树。数组存储优点:查询效率高,插入删除效率低链表特点:查询效率低,插入删除效率高在HasnMap底层,数组加上(链表或红黑树)的结构完美解决数组和链表的问题,使得查询、插入、删除的效率非常高。HashMap的哈希表是一种懒加载机制。创建HashMap存储元素的过程首先是将k和v放入Node对象(节点)中调用k。hasCode()方法提取哈希值;元素中存储的下标是对hashcode值和数组长度取模得到的。这时,有两种情况。如果下标位置没有元素,则直接将元素边输入到已有元素中,判断位置。元素是否等于当前元素,使用equals进行比较(默认是比较两个对象的地址)。如果两者相等,则直接覆盖,如果不相等(Hash碰撞)使用链表的结构将元素存储在原元素下(如果链表已经存在,则将其插入到链表的末尾),每个元素节点都有一个next属性指向下一个节点,由数组结构变为数组+链表;因为链表中的元素过多,会影响查找效率,所以当链表中的元素个数达到8个时,使用链表存储将改为使用红黑树存储(当节点数在红黑树上小于6,红黑树又会变成单向链表数据结构),原因是红黑树是平衡二叉树,其搜索性能高于聊天表的HashMap值实现首先调用k的hashCode()方法获取hash值,通过hash算法转换为数组下标。将hash值转化为数组下标后,通过数组定位下标位置。如果改变的位置没有任何内容,则范围为空;如果该位置存在单向链表,则取参数K和单向链表上每个节点的K进行相等比较,如果全部相等返回false,则返回null,如果有节点K并且参数K通过equals返回true,那么此时节点的值就是要获取的值。扩容HashMap中有两个重要的参数,初始容量和加载因子。,会返回空表,thershold(扩容阈值)为0,所以第一次扩容时默认值为16,负载因子默认为0.75。将数组容量乘以负载因子得到一个值。一旦数组中存储的元素个数超过这个值,就会调用rehash方法使数组的容量增加一倍,阈值也会增加一倍。扩容的时候会生成一个新的数组,所有的原始数据都需要重新计算哈希码值,重新赋值给一个新的数组,所以扩容操作对性能的消耗很大。所以,如果知道要存储的数据量比较大,那么在创建的时候就可以指定一个比较大的数据容量。也会引发一个问题,HashMap是先插入??还是先扩容:HashMap初始化后第一次插入数据时,先Resize扩容,再插入数据。之后,只要插入数据的数量达到阈值,就会发生调整大小。此时先插入数据,然后在插入元素之前或插入元素之后进行resizeHashMap中的扩容。在JDK1.7中,扩展是在插入元素之前进行的。JDK1.8中是先添加元素再判断是否展开。存储元素超过阈值是否会扩容?在JDK1.7中不一定,只有存储元素超过阈值并且当前存储位置不为null,才会进行扩容。在JDK1.8中,会进行扩充。HashMap和HashTable的区别在于线程。HashMap不是线程安全的,HashTable是线程安全的。在Hashtable的实现方法中加入了synchronized关键字来保证线程同步,所以HashMap的性能相对要高一些。如果在正常使用中没有特殊需求,我们推荐使用HashMap。如果在多线程环境下使用HashMap,则需要使用Collections。synchronizedMap()方法用于获取线程安全的集合。HashMap的key可以为null,HashTable的key不能为null。HashMap是Map接口的实现。HashTable实现了Map接口和Dictionary抽象类。容量为11,两者的填充因子默认都是0.75。HashMap扩容时,当前容量翻倍:capacity*2,-Hashtable扩容时,容量翻倍+1,即:capacity*2+1HashMap中hashcode的生成方法调用对象key的hashCode方法,然后对这个hashcode方法进行一些右移和异或运算(让hashCode的高位和低位都参与运算);通过右移和异或运算,可以使hashMap的哈希运算变得更加高效强,提高hashMap的get方法的效率为什么要使用HashCode?HashCode的存在主要是为了方便查找。HashCode用于确定对象在哈希存储结构中的存储地址(用hashcode表示对象在哈希表中的位置),hashCode存在的重要原因之一是在HashMap(HashSet)其实就是HashMap)(其实Object类的hashCode方法注释已经说明了),HashMap之所以快是因为它使用了哈希表,根据key和hashcode值生成数组下标(直接通过搜索equals方法和hashcode的关系总结一下:如果equals(Objectobj)方法被重写,则需要大量的内存,相当于用空间换取时间)重写hashCode()方法。如果两个对象equals(Objectobj)返回true,那么hashCode()也必须返回相同的int数。如果两个对象equals(Objectobj)返回false,则hashCode()不一定返回不同的int数字。如果两个对象hashCode()返回相同的int数字,equals(Objectobj)不一定返回true。如果两个对象hashCode()返回的是不同的int数,equals(Objectobj)必须返回false,如果在执行过程中集合中已经存入了同一个对象,影响hashCode值的相关信息不能修改,否则会造成内存泄漏.key为null怎么办当key为null时,只会放在hashMap的0位置(即key的hashCode为0,取数组长度余数后的下标为也为0),就不会有链表。在HashMap源码中,put方法处理的是null。key为null后,进入putForNullKey(Vvalue)方法,li中的for循环是在talbe[0]链表中寻找key为null的元素,找到则重新赋值给这个元素的值,并返回原值如果没有找到,将这个元素添加到talbe[0]链表的头部/***HashMapputmethod*/publicVput(Kkey,Vvalue){if(table==EMPTY_TABLE){inflateTable(threshold);}//键为空调用putForNullKey(value)if(key==null)returnputForNullKey(value);inthash=hash(key);inti=indexFor(hash,table.length);for(Entry