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 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 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

你可能感兴趣的:(LRU算法实现)