LRU算法实现

LRU算法,实现了put,remove(int),remove(),setMaxLength(int)四种方法。
  • put:put方法为向缓存中加入一个元素,若元素存在,则提到头结点。
  • remove int:移除指定缓存数据,并返回移除结果(boolean)。
  • remove:移除最后一个元素。
  • seMaxLength:设置缓存的大小。若新值比旧有元素的空间小,要删除末尾元素。
  1. 这里的写法我避免了一种情况,就是缓存空间大小为0或1的情况。首先缓存实现也没有只能放0或1个元素的情况;就算假设有这种情况,加个判断即可,我这里实现了仅仅是基础代码,欢迎大佬修改指正。
  2. 还有就是,我这里有很多冗余代码,没有抽离出来单独放到一个方法中(比如设置头结点等),是为了方便理解
1. 维护的内部类
    /* 维护一个双向链表 */
    static class Node {
        Node pre;
        Node next;
        int val;

        public Node(int val) {
            this.val = val;
        }
    }
2. 构造代码块与构造方法
    Node head;
    Node tail;

    HashMap<Integer, Node> cacheMap;
    private int maxLength;

    public NewLRU(int n) {
        if (n <= 1) {
            this.maxLength = 8; // 默认为8
        } else {
            this.maxLength = n;
        }
        cacheMap = new HashMap<>(maxLength);
    }
3. put方法,加入一个元素,没有就添加,有就直接提到head,多出来就删除。注意别忘了cacheMap的增删
    /* 向LRU结构中中添加val值 */
    public void put(int val) {
        if (head == null) {
            // 此时没有元素,直接添加
            Node node = new Node(val);
            head = tail = node;
            cacheMap.put(val, node);

        } else {
            // 有元素,判断是否已经有了
            if (cacheMap.containsKey(val)) {
                Node node;
                // 若 == head 则不用更新
                if ((node = cacheMap.get(val)) != head) {
                    if (node == tail) { // 若刚好为尾结点(此处已经不为head,说明至少2个元素)
                        tail = node.pre;
                        // 断开
                        tail.next = null;

                    } else { // 此处既不是head也不是tail,说明至少3个元素
                        // 断开
                        node.pre.next = node.next;
                        node.next.pre = node.pre;
                    }
                    node.pre = null;
                    // 插到头,并更新head
                    node.next = head;
                    head.pre = node;
                    head = node;
                }

            } else { // 没有元素,则创建并插入,插入结束后判断是否超过maxLength
                Node node;
                // 判断数量,如果够maxLength个,就直接修改tail.val并插到头结点,节省创建对象时间
                if (cacheMap.size() >= maxLength) {
                    node = tail;
                    // 可以不新建Node,但一定要在cache中移除此节点,否则会出现key和对应节点.val不一致的情况
                    cacheMap.remove(node.val);
                    node.val = val;
                    // 更新tail
                    tail = node.pre;
                    // 尾结点断开,并插到头
                    tail.next = null;
                    node.pre = null;

                } else { // 不足maxLength个就创建并插入头
                    node = new Node(val);

                }
                // 插入head前
                node.next = head;
                head.pre = node;
                head = node;
                cacheMap.put(val, node);
            }
        }
    }
4. 移除指定元素
    /* 移除指定元素 */
    public boolean remove(int val) {
        if (cacheMap.containsKey(val)) {
            Node node = cacheMap.get(val);
            cacheMap.remove(val);
            if (node == head) { // 可能只有1个元素
                head = head.next;
                if (head != null) head.pre = null;

            } else if (node == tail) { // 不为head,且为tail,至少2个元素
                tail = tail.pre;
                tail.next = null;

            } else { // 至少3个元素
                // 断开
                node.pre.next = node.next;
                node.next.pre = node.pre;
                node.pre = null;
                node.next = null;
            }
            return true;
        }
        return false;
    }
5. 移除最后一个元素
    /* 移除一个元素 */
    public Integer remove() {
        if(head == null) {
            return null;
        }

        int res = tail.val;
        cacheMap.remove(res);
        if (tail == head) {
            tail = head = null;
            return res;
        }
        // 否则说明有至少2个元素
        Node tmp = tail;
        tail = tail.pre;
        tmp.pre = null;
        tail.next = null;
        return res;
    }
6. 更新缓存大小
    /* 更新最大值 */
    public void setMaxLength(int newLength) throws Exception {
        if (newLength <= 1) {
            throw new Exception("缓存空间应该设置大于1");
        }
        // 若新长度更小,则要考虑删除元素
        if (newLength < cacheMap.size()) {
            int cnt = cacheMap.size() - newLength;
            while (cnt-- > 0) {
                cacheMap.remove(tail.val);
                tail = tail.pre;
            }
            // 断开
            tail.next.pre = null;
            tail.next = null;
        }

        this.maxLength = newLength;
    }

测试结果

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        Node tmp = head;
        while (tmp != null) {
            sb.append(tmp.val).append(" -> ");
            tmp = tmp.next;
        }
        sb.append("null\n cacheMap: {");
        if (cacheMap.size() > 0) {
            for (Map.Entry<Integer, Node> entry : cacheMap.entrySet()) {
                sb.append(entry.getKey()).append('=').append(entry.getValue().val).append(',').append(' ');
            }
            sb.delete(sb.length() - 2, sb.length());
            sb.append('}');
        } else {
            sb.append("null}");
        }

        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
        NewLRU lru = new NewLRU(5);
        System.out.println(lru.toString()); // 54321
        lru.put(1);
        System.out.println(lru.toString()); // 15432
        lru.put(4);
        System.out.println(lru.toString()); // 41532
        lru.put(6);
        System.out.println(lru.toString()); // 64153
        lru.remove(1);
        System.out.println(lru.toString()); // 6453
        lru.setMaxLength(3);
        System.out.println(lru.toString()); // 645
        lru.setMaxLength(5);
        lru.put(5);
        System.out.println(lru.toString()); // 564
        lru.put(6);
        System.out.println(lru.toString()); // 654
        lru.put(7);
        System.out.println(lru.toString()); // 7654
        lru.put(5);
        System.out.println(lru.toString()); // 5764
        lru.remove();
        System.out.println(lru.toString()); // 576
        lru.remove();
        System.out.println(lru.toString()); // 57
        lru.remove();
        System.out.println(lru.toString()); // 5
        lru.remove();
        System.out.println(lru.toString()); // null
    }

// 结果如下所示
null
 cacheMap: {null}
1 -> null
 cacheMap: {1=1}
4 -> 1 -> null
 cacheMap: {1=1, 4=4}
6 -> 4 -> 1 -> null
 cacheMap: {1=1, 4=4, 6=6}
6 -> 4 -> null
 cacheMap: {4=4, 6=6}
6 -> 4 -> null
 cacheMap: {4=4, 6=6}
5 -> 6 -> 4 -> null
 cacheMap: {4=4, 5=5, 6=6}
6 -> 5 -> 4 -> null
 cacheMap: {4=4, 5=5, 6=6}
7 -> 6 -> 5 -> 4 -> null
 cacheMap: {4=4, 5=5, 6=6, 7=7}
5 -> 7 -> 6 -> 4 -> null
 cacheMap: {4=4, 5=5, 6=6, 7=7}
5 -> 7 -> 6 -> null
 cacheMap: {5=5, 6=6, 7=7}
5 -> 7 -> null
 cacheMap: {5=5, 7=7}
5 -> null
 cacheMap: {5=5}
null
 cacheMap: {null}

Process finished with exit code 0

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容