声明,本文用得是jdk1.8php
前面已经讲了Collection的总览和剖析List集合以及散列表、Map集合、红黑树还有HashMap基础了:html
本篇主要讲解LinkedHashMap~java
看这篇文章以前最好是有点数据结构的基础:算法
固然了,若是讲得有错的地方还请你们多多包涵并不吝在评论去指正~微信
LinkedHashMap数据结构图:网络
ps:图片来源网络,侵删~数据结构
首先咱们来看看类继承图:post
我简单翻译了一下顶部的注释(我英文水平渣,若是有错的地方请多多包涵~欢迎在评论区下指正)测试
从顶部翻译咱们就能够概括总结出HashMap几点:spa
同时也给我带了几个疑问:
但愿能够在看源码的过程当中能够解决掉我这两个疑问~那接下来就开始吧~
下面我列举就这两个比较重要的:
这就印证了咱们的LinkedHashMap底层确确实实是散列表和双向链表~
LinkedHashMap.Entry
再也不是Node
.能够发现,LinkedHashMap有5个构造方法:
下面咱们来看看构造方法的定义是怎么样的:
从构造方法上咱们能够知道的是:LinkedHashMap默认使用的是插入顺序
本来我是想要找put方法,看看是怎么实现的,后来没找着,就奇了个怪~
再顿了一下,原来LinkedHashMap和HashMap的put方法是同样的!LinkedHashMap继承着HashMap,LinkedHashMap没有重写HashMap的put方法
因此,LinkedHashMap的put方法和HashMap是同样的。
若是没看过HashMap就是这么简单【源码剖析】的同窗,可进去看看~
固然了,在建立节点的时候,调用的是LinkedHashMap重写的方法~
get方法也是多了:判断是否为访问顺序~~~
讲到了这里,感受咱们能够简单测试一波了:
首先咱们来看看已插入顺序来进行插入和遍历:
public static void insertOrder() {
// 默认是插入顺序
LinkedHashMap<Integer,String> insertOrder = new LinkedHashMap();
String value = "关注公众号Java3y";
int i = 0;
insertOrder.put(i++, value);
insertOrder.put(i++, value);
insertOrder.put(i++, value);
insertOrder.put(i++, value);
insertOrder.put(i++, value);
//遍历
Set<Integer> set = insertOrder.keySet();
for (Integer s : set) {
String mapValue = insertOrder.get(s);
System.out.println(s + "---" + mapValue);
}
}
复制代码
测试一波:
接着,咱们来测试一下以访问顺序来进行插入和遍历:
public static void accessOrder() {
// 设置为访问顺序的方式
LinkedHashMap<Integer,String> accessOrder = new LinkedHashMap(16, 0.75f, true);
String value = "关注公众号Java3y";
int i = 0;
accessOrder.put(i++, value);
accessOrder.put(i++, value);
accessOrder.put(i++, value);
accessOrder.put(i++, value);
accessOrder.put(i++, value);
// 遍历
Set<Integer> sets = accessOrder.keySet();
for (Integer key : sets) {
String mapValue = accessOrder.get(key);
System.out.println(key + "---" + mapValue);
}
}
复制代码
代码看似是没有问题,可是运行会出错的!
前面在看源码注释的时候咱们就发现了:在AccessOrder的状况下,使用get方法也是结构性的修改!
为了简单看出他俩的区别,下面我就直接用key来进行看了~
如下是访问顺序的测试:
public static void accessOrder() {
// 设置为访问顺序的方式
LinkedHashMap<Integer,String> accessOrder = new LinkedHashMap(16, 0.75f, true);
String value = "关注公众号Java3y";
int i = 0;
accessOrder.put(i++, value);
accessOrder.put(i++, value);
accessOrder.put(i++, value);
accessOrder.put(i++, value);
accessOrder.put(i++, value);
// 访问一下key为3的元素再进行遍历
accessOrder.get(3);
// 遍历
Set<Integer> sets = accessOrder.keySet();
for (Integer key : sets) {
System.out.println(key );
}
}
复制代码
测试结果:
如下是插入顺序的测试(代码就不贴了,和上面几乎同样):
咱们能够这样理解:最经常使用的将其放在链表的最后,不经常使用的放在链表的最前~
这个知识点以个人理解而言,它这个访问顺序在LinkedHashMap若是不重写用处并不大~它是用来给别的实现进行扩展的
removeEldestEntry(Map.Entry<K,V> eldest)
方法,重写它能够删除最久未被使用的元素!!afterNodeInsertion(boolean evict)
方法,新增时判断是否须要删除最久未被使用的元素!!去网上搜了几篇资料,都是讲LRUMap的实现的(也就是对LinkedHashMap进行扩展),有兴趣的同窗可参考下列连接:
对于remove方法,在LinkedHashMap中也没有重写,它调用的仍是父类的HashMap的remove()
方法,在LinkedHashMap中重写的是:afterNodeRemoval(Node<K,V> e)
这个方法
固然了,在remove的时候会涉及到上面重写的方法:
Set<Map.Entry<K,V>> entrySet()
是被重写的了
看到了这里,咱们就知道为啥注释说:初始容量对遍历没有影响
由于它遍历的是LinkedHashMap内部维护的一个双向链表,而不是散列表(固然了,链表双向链表的元素都来源于散列表)
LinkedHashMap比HashMap多了一个双向链表的维护,在数据结构而言它要复杂一些,阅读源码起来比较轻松一些,由于大多都由HashMap实现了..
阅读源码的时候咱们会发现多态是无处不在的~子类用父类的方法,子类重写了父类的部分方法便可达到不同的效果!
newNode()
方法重写了。LinkedHashMap调用父类的put方法,里面回调的是重写后的newNode()
,从而达到目的!LinkedHashMap能够设置两种遍历顺序:
对于访问顺序,它是LRU(最近最少使用)算法的实现,要使用它要么重写LinkedListMap的几个方法(removeEldestEntry(Map.Entry<K,V> eldest)
和afterNodeInsertion(boolean evict)
),要么是扩展成LRUMap来使用,否则设置为访问顺序(access-ordered)的用处不大~
LinkedHashMap遍历的是内部维护的双向链表,因此说初始容量对LinkedHashMap遍历是不受影响的
参考资料:
明天要是无心外的话,可能会写TreeMap,敬请期待哦~~~~
文章的目录导航:zhongfucheng.bitcron.com/post/shou-j…
若是文章有错的地方欢迎指正,你们互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同窗,能够关注微信公众号:Java3y。为了你们方便,刚新建了一下qq群:742919422,你们也能够去交流交流。谢谢支持了!但愿能多介绍给其余有须要的朋友