阿里代码评测:手撕LUR

题目描述

//评测题目:
// 设计和构建一个“最近最久未使用”缓存(LRU),该缓存会删除最近最久未使用的项目。
// 缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。
// 当缓存被填满时,它应该删除最近最久未使用的项目。

// 它应该支持以下操作: 获取数据 get 和 写入数据 put 。

// 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
// 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值,如果存在则覆盖数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最久未使用的数据值,从而为新的数据值留出空间

要求:可以使用基本Map,不得直接使用LinkedHashMap处理

个人思路

题解是key-value形式,所以底层可直接用HashMap做get,set。同时用双向列表来记录活跃顺序,两个不保存数据的空节点head和tail,可以更方便处理。map的value 存放链表节点。

代码

import java.util.HashMap;
import java.util.Map;

public class MyLRU {

    private final int maxLength;
    private int size = 0;

    private final Node head = newNode(null, null);
    private final Node tail = newNode(null, null);


    private Map<Object, Node> map = new HashMap<>();

    public static class Node {
        public Object key;
        public Object value;
        public Node next;
        public Node pre;
    }

    MyLRU(int maxSize) {
        this.maxLength = maxSize;
        head.next = tail;
        tail.pre = head;
    }

    public void put(Object key, Object value) {
        if (!map.containsKey(key)) {
            Node e = newNode(key, value);
            addNode(e);
            map.put(key, e);
        } else {
            Node e = map.get(key);
            e.value = value;
            flush(e);
        }
    }

    public Object get(Object key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        Node e = map.get(key);
        flush(e);
        return e.value;
    }

    private Node newNode(Object key, Object value) {
        Node node = new Node();
        node.key = key;
        node.value = value;
        return node;
    }

    private void flush(Node e) {
        e.pre.next = e.next;
        e.next.pre = e.pre;
        tail.pre.next = e;
        e.pre = tail.pre;
        e.next = tail;
        tail.pre = e;
    }

    private void addNode(Node e) {
        e.pre = tail.pre;
        e.next = tail;
        tail.pre.next = e;
        tail.pre = e;
        if (size == maxLength) {
            map.remove(head.next.key);
            head.next.next.pre = head;
            head.next = head.next.next;
        } else {
            size++;
        }
    }
}

并发场景下问题

最后面试官又针对代码并发情况下的安全问题问了下,以及怎么解决:
1、hashmap本身的线程安全性问题,可以改为ConcurrentHashMap;
2、指针操作本身有线程安全问题,会导致活跃节点丢失和顺序不一致。写写场景下,活跃节点覆盖;写读场景下,会导致可能可能淘汰了刚读的数据;读读场景下,活跃顺序与读取顺序可能不一致,容量小时淘汰影响大,容量大时影响较小。考虑对代码块进行sychronized同步处理,用类锁。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值