HashMap:
Hashmap的底层使用的是一种叫做哈希表(也成为散列表)的数据结构。哈希表其实是一个数组,但是这个数组里面的元素不是int类型,也不是double类型,而是链表。也就是说每一个数组元素都是一个链表,而每一个链表上面装的都是一个个连续的节点(node)。这种数据结构将数组查询效率高的特点和链表增删效率高的特点巧妙结合在一起,成为了一种独特并且效率高的数据结构。
HashMap添加元素的方法 map.put(k,v) 详解:
第一步:先将key和value封装到Node对象当中。
第二步:底层调用hashCode()方法,通过key来计算出哈希值。
第三步:然后将通过哈希函数or哈希算法将哈希值转换成数组的下标。
第四步:如果数组当前下标没有链表的话,那么就直接将node节点放到对应的下标上面。如果数组当前下标有链表的话,那么要将key的值和链表上面的每一个node节点中的key值进行equals方法的比较,如果返回的所有结果都是false,那么就将这个新的节点插入到这条链表的末尾。如果返回的结果中存在true,那么这个对应的true的这个节点就会被我们要插入的节点覆盖。
HashMap添加元素的方法 map.get(k) 详解:
第一步:根据hashCode()方法,算出key的哈希值。
第二步:然后通过哈希算法,得到key所对应的数组元素下标。
第三步:如果数组当前下标所对应的位置上没有链表,那么直接返回null值,如果有链表,那么就将key值和链表上面每一个节点的key值进行equals方法的比较,如果都返回false,那么get方法也返回null,如果有一个返回true,那么get方法就会返回那个节点所对应的value值。
注意:HashMap集合key部分的元素需要重写equals方法。同理,如果加入到HashMap中的key部分,其实就是加入到HashSet中的。
HashMap集合的key部分特点:无序且不可重复
原因:首先我们所加入的键值对,不一定会被加入到哪个链表上面,取出来的顺序不一定是根据我们存进去的顺序是一样的。而同时,equals方法保证了key的值是不会重复的。
因此,无论是加入到HashMap集合中的key部分还是加入到HashSet中,我们都需要重写equals和hashCode方法。
equals默认比较的是两个对象的内存地址,而我们应该比较的是内容。如果我们不重写equals,默认这个方法默认比较的是内存地址,而我们想比较的其实是两个节点的内容。另外如果我们重写equals的同时必须也重写hashCode()方法。
当两个节点的equals比较结果为true的时候,也就代表两个节点是在同一个链表上面,而我们都知道确定节点在哪个链表上面是由哈希算法和哈希值得到的结果。由此我们可以知道这两个节点通过hashCode()和哈希算法的计算所得到的坐标值应该是一样的,由此我们可以判断当equals方法返回true的时候,其实代表hashCode()的值是一样的。但是这一切都是在重写equals()方法和hashCode()方法上。
HashMap的底层使用情况:HashMap的底层是哈希表数据结构并且是非线程安全的。在JDK8之后,如果哈希表单向链表中的元素超过了8,那么就自动将单向链表变为红黑树这种数据结构。而如果红黑树中的节点小于6的时候,就会自动把红黑树变回到单向链表。需要注意的是HashMap的初始容量为16,默认加载因子为0.75,就是说只有到里面的元素(节点)数量超过数组length的75%之后,HashMap底层才会选择扩容,扩容的时候是扩容二倍,然后重新计算各个元素节点的位置。
HashMap的两种遍历方法: