之前有研究Java源码中HashMap的put()方法的实现,后来又看了HashMap的整体数据结构,记录一下
什么是HashMap
经常用到HashMap,用来存储数据,在某些框架中可以替代javabean,主要特点如下:
- key-value形式的键值对
- 非线程安全的
- 存储数据的数据和插入顺序无关
- key可以为null
其中和HashMap差不多但是是线程安全并且key不能为null的类是HashTable,但是很少用,一般都是用ConcurrentHashMap.
内部结构
我们都知道HashMap内部都是一组组键值对,那么这个键值对在内部是什么形式存在的呢?
其实是 Node[] ,即节点数组的形式保存.就是说HashMap其实是一个数组,每一个存进去的键值对是数组中的一个元素.
那么有几个问题.
- 我new一个HashMap的时候并没有指定这个”数组”的大小
查看源码可以知道,刚new出来的时候HashMap并没有设置容量,是在调用put()方法的时候检查,如果这个HashMap的table,即Node[]数组是null,那么初始化一下,经过一系列的方法产生一个有默认容量( DEFAULT_INITIAL_CAPACITY=16 )的Node[]数组
- 我一直放键值对没有出现数组下标越界的异常啊
因为HashMap中有一个常量叫加载因子( DEFAULT_LOAD_FACTOR=0.75 ),当当前元素的数量占容量的百分比大于动态因子的时候,就会进行数组的自动扩容,这是自动维护的,所以没有感觉
- Node是什么
还记得HashMap的遍历吗?
Map<String, String> map = new HashMap<String, String>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
for (Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
}
HashMap的遍历可以看出,里面的键值对是以Entry的形式存在,那么Node和Entry什么关系?
其实,Entry是Map接口中的内部接口Entry,而Node是Entry的实现类.
- 那么键值对是怎么放入HashMap中的
我们都知道,放入数组需要指定下标.那么这个下标是什么?在HashMap中是和这个键的hash值有关,在hash值经过某种算法后得到的下标,然后将键值对放入数组中
- 如果键的hash值重复了怎么办?
在插入的时候,我们知道同一个键插入不同的值时,会用新的值替换旧的值,并将旧的值返回.但是,有时候会出现不同的键得到相同的hash值,这个就尴尬了,那么内部是怎么解决的.
这个我们可以看一下Node类的成员变量
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
fianl K key;
V value;
Node<K, V> next;
...
}
重点是这个next是一个新的Node,这是什么,这是链表啊!也就是说,出现这种情况了,那么会在Node[]的一个位置放着一个链表,里面是重复的hash值的Node.
ps:如果链表过长,则会转化成HashMap内部的红黑树TreeNode,这样可以提高性能
以上就是HashMap的简单介绍,可能有什么问题,发现了记得评论,谢谢.有什么新的想法会不定期更新…