HashMap集合
HashMap集合的特点
首先我们知道HashMap集合底层是一个由数组+链表+红黑树的一个结构(JDK8更新之后),也可以说是哈希表
HashMap集合有几个特点
- 无序且不可重复
- 放在hashMap集合中的key和value都可以为null
在Java8中,将HashMap底层由原先的Entry节点更改为现在的Node节点,插入格式也由头插法变为了尾插法,之前的Java7的头插法在插入的时候会将原位置的数据向后移一位,再插入数据到该位置;有可能出现逆序和环形链表死循环的问题。Java8改为尾插法解决了这一问题,增加了效率
集合中的key是不能重复的而且无序
// public class HashMapTest01 {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1111,"张三");
map.put(2222,"李四");
map.put(3333,"赵六");
map.put(1111,"国王");//key重复的时候value会自动覆盖
System.out.println(map.size());//3
Set<Map.Entry<Integer, String>> entries = map.entrySet();
for(Map.Entry<Integer,String> s : entries){
System.out.println(s.getKey() + s.getValue());
//验证HashMap集合中的key部分元素:无序不可重复
}
}
}
输出结果:
3
3333赵六
1111国王
2222李四
HashMap底层的源代码(只贴出需要的属性)
下面展示一些 内联代码片
。
// A code block
var foo = 'bar';
// public class HashMap{
//HashMap底层实际上就是一个数组
Node<K,V>[] table;
//静态内部类HashMap,Node
static class Node<K,V>{
final int hash;// 哈希值(哈希值是key的hashcode()方法执行结果。hash值通过哈希函数,可以转化为数组的下标)
final k key; //存储到Map集合中的key
V value; //存储到Map集合中的value
Node<K,V> next;//下一个节点的内存地址
}
以上是HashMap中比较重要的几个属性
我们用put方法和get方法来举例说明再存储的时候底层所进行的内容
map.put(K,V)方法底层实现原理
第一步:先将(K,B)封装为一个Node对象
第二步:底层会调用key和hashcode()方法来计算出hash值,再通过哈希函数/哈希算法计算出hash值所对应的数组下标是多少
第三步:找到下标之后会先判断,如果下标位置没有任何元素,那么直接将Node对象插入到这个位置上。如果该位置有链表,此时k会和链表上的每一个key进行equals()方法比较,如果equals方法返回true,那么就会用新的value值覆盖老的value值,如果equals方法返回false,那么就会再链表的尾部插入这个Node对象。get()方法也是同理了
从以上的步骤可以看出,HashMap在put元素的时候,key会先后调用hashcode()和equals()方法
那么这两个方法为什么需要同时重写呢,我们通过代码来讲解一下
//
public class HashMapTest02 {
public static void main(String[] args) {
Student s1 = new Student("张三");
Student s2 = new Student("张三");
/*//重写equals方法之前是false
System.out.println(s1.equals(s2));//false*/
//重写equals方法之后是true
System.out.println(s1.equals(s2));//true
System.out.println("s1的hashcode值" + s1.hashCode());//s1的hashcode值1735600054
System.out.println("s2的hashcode值" + s2.hashCode());//s2的hashcode值21685669
//没有重写hashcode方法
Set<Student> set =new HashSet<>();
set.add(s1);
set.add(s2);
System.out.println(set.size());//这个结果按说应该是1,但是结果是2。显然不符合hashSet集合的规范
输出结果
true
s1的hashcode值774920
s2的hashcode值774920
2
}
}
以上代码只是简单的new了一个Student对象,来讲解一下为什么要同时重写hashcode()方法和equals()方法
最终结论:放在HashMap集合中的key和hashSet集合中的元素
必须同时重写hashcode和equals方法
最后HashMap有一个扩容的机制,需要大家了解一下 面试的时候可能会问到。
HashMap集合默认的初始化容量为16,负载因子为0.75
当数组的的容量达到 初始化容量 * 负载因子
会调用resize()方法进行扩容 扩容为原来的2倍.
还有就是ConcurrentHashMap了感兴趣的可以去了解一下,现在基本都在用这个实现类因为这个实现类并发控制采用的是synchronized+CAS来操作,采用的是分段上锁,而不是像hashtable一样在方法上上锁,效率很低。数据结构和HashMap一样。