说明
虽然很多人做过这种讲解的文章、但是自己点进源码看里面的结构的感受是不一样的。
本章做一个HashTable的简单的总结、是jdk1.8版本的。
继承图
从图上可以看出一个简单的继承关系、主要是对LikedHashMap简单源码的探究
抛出问题
- 内部数据结构是怎么样的?
- 是否有扩容、扩容机制是什么时候扩容的、扩容多少?
- key或value是否能为空。
- 是否线程安全?
创建一个LinkedHashMap集合对象
Map<String,Integer> map = new LinkedHashMap<>();
属性
// 双链表头
transient LinkedHashMap.Entry<K,V> head;
// 双链表尾
transient LinkedHashMap.Entry<K,V> tail;
// 用于访问顺序
final boolean accessOrder;
transient关键字在序列化的时候不会被序列化、
accessOrder:属性在这里的作用是、如果为true、则获取的数据会在链表后面、为false则不会
例子
public static void main(String[] args) {
Map<String,Object> map = new LinkedHashMap<>(16, 0.75f, true);
map.put("1","2");
map.put("2","2");
map.put("3","2");
map.put("4","2");
map.get("1");
map.get("2");
System.out.println(map.toString());
Map<String,Object> map1 = new LinkedHashMap<>(16, 0.75f, false);
map1.put("1","2");
map1.put("2","2");
map1.put("3","2");
map1.put("4","2");
map1.get("1");
map1.get("2");
System.out.println(map1.toString());
}
输出结果
{3=2, 4=2, 1=2, 2=2}
{1=2, 2=2, 3=2, 4=2}
构造器
public LinkedHashMap() {
// 调用的是HashMap的构造
super();
// 给存取顺序 赋值
accessOrder = false;
}
public LinkedHashMap(Map<? extends K, ? extends V> m) {
// 调用的是HashMap的构造
super();
// 给存取顺序 赋值
accessOrder = false;
// 调用了HashMap中的方法
putMapEntries(m, false);
}
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
// 调用的是HashMap的构造
super(initialCapacity, loadFactor);
// 给存取顺序 赋值
this.accessOrder = accessOrder;
}
可以看一个第二个构造器中的putMapEntries方法
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {
if (table == null) { // pre-size
float ft = ((float)s / loadFactor) + 1.0F;
int t = ((ft < (float)MAXIMUM_CAPACITY) ?
(int)ft : MAXIMUM_CAPACITY);
if (t > threshold)
threshold = tableSizeFor(t);
}
else if (s > threshold)
resize();
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value, false, evict);
}
}
}
点入到这个源码中会发现是调用的是HashMap中的方法、会发现他会初始化一些HashMap的需要的值、存储不够会调用扩容的方法、添加也是调用HashMap的方法一个一个遍历添加。
对集合CRUD简单说明
增
Map<String,Object> map = new LinkedHashMap<>();
map.put("key","1");
他其实调用的是HashMap的中添加方法、可以去看看我写的HashMap的添加方法解析
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
虽然他是调用的HashMap中的添加方法、但是他的Node节点创建的是LinkedHashMap节点的对象
可以得出一个结论存储的数据在HashMap的table中也有、只不过里面存储的对象是LinkedHashMap.Entry节点对象、可以自行debug调试看结果。
删
Map<String,Object> map = new LinkedHashMap<>();
map.put("1","2");
System.out.println(map.remove("1"));
他调的也是HashMap中的remove() 实现方法
他会去调用一个LinkedHashMap中的afterNodeRemoval()方法来删除替换节点的顺序。
改
改和增加是一样的他也是、通过key的hash值来找到对应位置、将其对应值覆盖
查
public V get(Object key) {
Node<K,V> e;
// getNode()是调用HashMap中的方法获取节点中的位置
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e); // 更换链表的位置
return e.value; // 返回获取的value
}
解决问题
- 内部数据结构是怎么样的?
- LinkedHashMap是HashMap的一个子类、基本的一些方法都是走hashMap来实现的、只不过是Node(节点)、对象不一样、插入顺序和取出顺序一样
双向链表
- LinkedHashMap是HashMap的一个子类、基本的一些方法都是走hashMap来实现的、只不过是Node(节点)、对象不一样、插入顺序和取出顺序一样
- 是否有扩容、扩容机制是什么时候扩容的、扩容多少?
- 走HashMap的机制
- key或value是否能为空。
- 可以、只允许一条记录的键为null,允许多条记录的值为null
- 是否线程安全?
- 线程不安全集合。
总结
LinkedHashMap是HashMap的一个子类、他基本走的都是HashMap的方法及扩容的方式、但是存储的Node节点对象不一样、就是双向链表、是指每一个节点都有一对前驱后续、头节点的前驱一般为NULL、尾节点的后续一般为NULL
双向链表百度百科
- 为什么Node节点对象不一样、可以往上看增加的方法那块写了。
结构图