1. 程式人生 > 程式設計 >Java實現簡單LRU快取機制的方法

Java實現簡單LRU快取機制的方法

一、什麼是 LRU 演算法

就是一種快取淘汰策略。

計算機的快取容量有限,如果快取滿了就要刪除一些內容,給新內容騰位置。但問題是,刪除哪些內容呢?我們肯定希望刪掉哪些沒什麼用的快取,而把有用的資料繼續留在快取裡,方便之後繼續使用。

LRU是Least Recently Used的縮寫,即最近最少使用,是一種常用的頁面置換演算法,選擇最近最久未使用的頁面予以淘汰。

二、LRU的使用

LRUCache cache = new LRUCache( 2 /* 快取容量 */ );
    cache.put(1,1);
    cache.put(2,2);
    cache.get(1);    // 返回 1
    cache.put(3,3);  // 該操作會使得金鑰 2 作廢

第一步:建立一個長度為2的LRUCache

Java實現簡單LRU快取機制的方法

第二步:cache.put(1,1);放入key=1,value=1的資料

Java實現簡單LRU快取機制的方法

第三步:cache.put(2,2);放入key = 2,value = 2的資料
(因為2剛使用,所有把2移動到前面)

Java實現簡單LRU快取機制的方法

第四步:cache.get(1);獲取key = 1的資料
(因為我們剛使用了1,所以把1移動到前面)

Java實現簡單LRU快取機制的方法

第五步:cache.put(3,3);放入key = 3,value = 3的資料
(因為3剛放進,所以放前面,又因為容量只有2,需要移除原先的1個。只因key = 2是最近最少使用的(key = 1剛get()過),所以移除2。

Java實現簡單LRU快取機制的方法

三、LRU的實現機制

演算法:

LRU 快取機制可以通過雜湊表輔以雙向連結串列實現,我們用一個雜湊表和一個雙向連結串列維護所有在快取中的鍵值對。

1)雙向連結串列按照被使用的順序儲存了這些鍵值對,靠近頭部的鍵值對是最近使用的,而靠近尾部的鍵值對是最久未使用的。

2)雜湊表即為普通的雜湊對映(HashMap),通過快取資料的鍵對映到其在雙向連結串列中的位置。

一、初始化:

Java實現簡單LRU快取機制的方法

二、cache.put(1,1):

Java實現簡單LRU快取機制的方法

三、cache.put(2,2):

Java實現簡單LRU快取機制的方法

四、cache.get(1):

Java實現簡單LRU快取機制的方法

五、cache.put(3,3):

Java實現簡單LRU快取機制的方法

四、程式碼如下

import java.io.*;
import java.util.HashMap;

public class test {

  public static void main(String args[]) throws IOException {

    LRUCache cache = new LRUCache( 2 /* 快取容量 */ );
    cache.put(1,3);  // 該操作會使得金鑰 2 作廢
    cache.get(2);    // 返回 -1 (未找到)
    cache.put(4,4);  // 該操作會使得金鑰 1 作廢
    cache.get(1);    // 返回 -1 (未找到)
    cache.get(3);    // 返回 3
    cache.get(4);    // 返回 4
  }
}

/**
 * 設計和實現一個 LRU (最近最少使用) 快取機制。它應該支援以下操作: 獲取資料 get 和 寫入資料 put
 */
class LRUCache {

  private HashMap<Integer,LinkedNode> cache = new HashMap();//方便通過key快速定位結點
  private int size;
  private int capacity;
  private LinkedNode head,tail;
  class LinkedNode{
    int key;
    int value;
    LinkedNode pre;
    LinkedNode next;
  }
  public LRUCache(int capacity) {
    this.size = 0;
    this.capacity = capacity;
    head = new LinkedNode();
    tail = new LinkedNode();
    head.next = tail;
    tail.pre = head;
  }

  /**
   * 移除節點
   * @param node
   */
  private void removeNode(LinkedNode node) {
    LinkedNode preNode = node.pre;
    LinkedNode nextNode = node.next;
    preNode.next = nextNode;
    nextNode.pre = preNode;
  }

  /**
   * 新增節點到頭部
   * @param node
   */
  private void addNode(LinkedNode node) {
    node.pre = head;
    node.next = head.next;
    head.next.pre = node;
    head.next = node;
  }

  /**
   * 將節點移動到頭部
   * @param node
   */
  private void moveToHead(LinkedNode node) {
    removeNode(node);
    addNode(node);
  }

  /**
   * 獲取資料
   * @param key
   * @return
   */
  public int get(int key) {
    LinkedNode node = cache.get(key);
    if(node != null) {
      moveToHead(node);
    }else{
      return -1;
    }
    return node.value;
  }

  /**
   * 寫入資料
   * @param key
   * @param value
   */
  public void put(int key,int value) {
    LinkedNode node = cache.get(key);
    //存在
    if(node != null) {
      node.value = value;//可能更新資料
      moveToHead(node);
    }
    //不存在
    else{
      LinkedNode newNode = new LinkedNode();
      newNode.key = key;
      newNode.value = value;
      cache.put(key,newNode);//更新Map
      addNode(newNode);//新增結點到頭部
      size++;//更新結點數
      if(size > capacity) {//如果結點數超過容量大小
        LinkedNode tailPre = tail.pre;
        cache.remove(tailPre.key);//更新Map
        removeNode(tailPre);//刪除最後一個結點(尾結點的前一個結點)
        size--;
      }
    }
  }
}

總結:自己實現的簡單LRU總歸太簡單了,要是想完善或者實現更真實的LRU,不妨參考一下Redis中的LRU。(◔◡◔)

到此這篇關於Java實現簡單LRU快取機制的方法的文章就介紹到這了,更多相關Java LRU快取內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!