散列表(下)

扫码关注公众号,获取更多内容

目录

一、LRU缓存淘汰算法

二、Java LinkedHashMap分析


一、LRU缓存淘汰算法

我们如何实现一个LRU缓存淘汰算法

如果单纯用链表实现LRU缓存淘汰算法的话,我们要怎么实现呢?

首先我们需要维护一个按照访问时间从大到小有序排序的链表结构,缓存的大小是有限的,当空间不够,需要淘汰一个数据的时候,我们就直接将链表头部的节点删除,当需要缓存某个数据的时候,先在链表中查找这个数据,如果没找到,就将该数据放在链表尾部,如果有,就把该数据移动到链表尾部,查找数据的过程是需要遍历链表的,所以单纯用链表实现LRU缓存淘汰算法的世界复杂度很高,是O(n)。

一个缓存系统主要包含下面几个操作:

1、往缓存中添加一个数据。

2、从缓存中删除一个数据。

3、从缓存中查找一个数据。

这三个操作都涉及“查找”操作,单纯使用链表,时间复杂度只能是O(n)。如果我们将散列表和链表两种数据结构组合使用,可以将时间复杂度降低至O(1)。具体结构如下图:

使用双向链表存储数据,链表中的每个结点处理存储数据(data)、前驱指针(prev)、后继指针(next)之外,还新增了一个特殊的字段hnext。

我们的散列表是通过链表法解决散列冲突的,所以每个结点都会在两条链中。一个链是双向链表,另一个链是散列表中的拉链前驱指针和后继指针是为了将结点串在双向链表中,hnext指针是为了将结点串在散列表的拉链中

首先,我们来看如何查找一个数据。通过散列表,我们可很快地在缓存中找到一个数据(散列表中查找数据的时间复杂度接近O(1))。当找到数据之后,我们需要将它移动到双向链表的尾部。

其次,我们来看如何删除一个数据。借助散列表,我们可以在O(1)时间复杂度里找到要删除的结点,因为链表是双向链表,双向链表删除结点的时间复杂度是O(1)。

最后,我们来看如何添加一个数据。我们需要先看这个数据是否已经存在缓存中,如果已经在其中,需要将其移动到双向链表的尾部,如果不在其中,需要考虑缓存有没有满,如果满了,则将双向链表的头部结点删除,然后再将数据放到链表的尾部,如果没有满,就直接将数据放到链表的尾部。

二、Java LinkedHashMap分析

我们先看一段代码,你觉得会以什么样的顺序打印3,1,5,2这几个key呢?

HashMap<Integer, Integer> m = new LinkedHashMap<>();
m.put(3, 11);
m.put(1, 12);
m.put(5, 23);
m.put(2, 22);

for (Map.Entry e : m.entrySet()) {
  System.out.println(e.getKey());
}

上方的代码会 按照插入的顺序依次来打印,打印出:3,1,5,2。理论上来说,散列表中的数据是经过散列函数打乱后无规律存储的,这里是如何实现按照数据插入顺序来遍历的呢?

其实,LinkedHashMap也是通过散列表和链表组合在一起实现的,它不仅支持按照插入顺序遍历数据,还支持按照访问顺序来遍历数据。我们来分析下面这段代码:

// 10是初始大小,0.75是装载因子,true是表示按照访问时间排序
HashMap<Integer, Integer> m = new LinkedHashMap<>(10, 0.75f, true);
m.put(3, 11);
m.put(1, 12);
m.put(5, 23);
m.put(2, 22);

m.put(3, 26);
m.get(5);

for (Map.Entry e : m.entrySet()) {
  System.out.println(e.getKey());
}

上述代码的打印结果是1,2,3,5。我们来具体分析下,为什么打印顺序是这样的:

当调用put向LinkedHashMap添加数据的时候,会将数据加在链表的尾部,所以,前四个put完成后,链表中的数据如下图:

在put(3,26)的时候,3这个键值已经存在了,将(3,,11)删除,将新的(3,26)放到链表的尾部,这时候的结构如下图:

当访问get(5)的时候,我们将被访问的数据移动到链表的尾部,链表中的数据如下所示:

所以最后打印出来的数据是1,2,3,5。

总结:LinkedHashMap是通过双向链表和散列表这两种数据结构组合实现的。LinkedHashMap中的“Linked”实际上指的是双向链表,并非指用链表法解决散列冲突

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值