浅谈Java如何实现一个基于LRU时间复杂度为O(1)的缓存

2025-05-29 0 53

LRU:Least Recently Used最近最少使用,当缓存容量不足时,先淘汰最近最少使用的数据。就像JVM垃圾回收一样,希望将存活的对象移动到内存的一端,然后清除其余空间。

缓存基本操作就是读、写、淘汰删除。

读操作时间复杂度为O(1)的那就是hash操作了,可以使用HashMap索引 key。

写操作时间复杂度为O(1),使用链表结构,在链表的一端插入节点,是可以完成O(1)操作,但是为了配合读,还要再次将节点放入HashMap中,put操作最优是O(1),最差是O(n)。

不少童鞋就有疑问了,写入时又使用map进行了put操作,为何缓存不直接使用map?没错,首先使用map存储了节点数据就是采用空间换时间,但是淘汰删除不好处理,使用map如何去记录最近最少使用(涉及到时间、频次问题)。so,使用链表可以将活跃节点移动到链表的一端,淘汰时直接从另一端进行删除。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100
public class LruCache<K,V> {

/** 这里简单点直接初始化了*/

private int capacity = 2;

private int size = 0;

private HashMap<K,DoubleListNode<K,V>> cache = new HashMap<>(capacity);

private DoubleListNode<K,V> lruNode = new DoubleListNode<K, V>(null,null,null,null);

private DoubleListNode<K,V> mruNode = new DoubleListNode<K, V>(null,null,null,null);

public V get(K key){

DoubleListNode<K,V> target = cache.get(key);

if (target == null) {

return null;

}

/** 使用过就移动到右侧 */

move2mru(target);

return target.value;

}

public void put(K key,V value){

if(cache.containsKey(key)){

DoubleListNode<K,V> temp = cache.get(key);

temp.value = value;

/** 使用过就移动到右侧 */

move2mru(temp);

return;

}

/** 容量满了清除左侧 */

if(size >= capacity){

evict4lru();

}

DoubleListNode<K,V> newNode = new DoubleListNode<>(mruNode,null,key,value);

if(size == 0){

lruNode.next = newNode;

}

mruNode.next = newNode;

mruNode = newNode;

cache.put(key,newNode);

size++;

}

private void move2mru(DoubleListNode<K,V> newMru){

DoubleListNode<K,V> pre = newMru.pre;

DoubleListNode<K,V> next = newMru.next;

pre.next = next;

newMru.pre = mruNode;

mruNode.next = newMru;

mruNode = newMru;

}

private void evict4lru(){

cache.remove(lruNode.next.key);

lruNode.next = lruNode.next.next;

size--;

}

public String toString(){

StringBuffer sb = new StringBuffer("lru -> ");

DoubleListNode<K,V> temp = lruNode;

while(temp!=null){

sb.append(temp.key).append(":").append(temp.value);

sb.append(" -> ");

temp = temp.next;

}

sb.append(" -> mru ");

return sb.toString();

}

public static void main(String[] args) {

LruCache<String,String> cache = new LruCache<>();

cache.put("1","1");

System.out.println(cache);

cache.get("1");

cache.put("2","2");

System.out.println(cache);

cache.put("3","3");

System.out.println(cache);

cache.put("4","4");

System.out.println(cache);

}

}

class DoubleListNode<K,V>{

K key;

V value;

DoubleListNode<K,V> pre;

DoubleListNode<K,V> next;

public DoubleListNode(K key,V value){

this.key = key;

this.value = value;

}

public DoubleListNode(DoubleListNode<K,V> pre,DoubleListNode<K,V> next,K key,V value){

this.pre = pre;

this.next = next;

this.key = key;

this.value = value;

}

}

这里使用链表,及HashMap完成了基于LRU缓存,其中HashMap主要用来快速索引key,链表用来完成LRU机制。当然尚有许多不足,包括缓存移除remove,缓存ttl,线程安全等。

到此这篇关于浅谈Java如何实现一个基于LRU时间复杂度为O(1)的缓存的文章就介绍到这了,更多相关Java基于LRU时间复杂度为O(1)的缓存内容请搜索快网idc以前的文章或继续浏览下面的相关文章希望大家以后多多支持快网idc!

原文链接:https://blog.csdn.net/weixin_43275277/article/details/107740004

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 浅谈Java如何实现一个基于LRU时间复杂度为O(1)的缓存 https://www.kuaiidc.com/119056.html

相关文章

发表评论
暂无评论