LRU算法 :學習筆記

LRU算法 :學習筆記

LRU是什麼

LRU(Least Recently Used)即最近最少使用,是一種緩存算法(頁面置換算法)。咱們知道,緩存一般是具備固定大小的,他應該只保存那些經常被用到的數據,而數據如何更新則是經過緩存算法實現,LRU算法就是一種簡單,經常使用的緩存算法。node

原理

LRU算法是核心思想是:若是一個數據在最近一段時間都沒有被用到,那麼它在未來被使用到的可能性也很小。故當緩存空間已滿的狀況下,咱們須要淘汰掉最久沒有被訪問到的數據。理想的LRU算法讀寫是時間複雜度應該都爲O(1)。算法

實現

爲了達到理想的性能,咱們須要一種既能夠按訪問順序排序,又能夠在常數時間隨機訪問的數據結構。這裏能夠採用HashMap和雙向鏈表實現。HashMap能夠存儲Key,能夠在常數時間裏讀寫Key,而Value用來指向雙向鏈表的節點,爲了在常數時間裏移除一個節點咱們還須要Head節點和Tril節點。緩存

  1. put(key,value)
    首先在HashMap中查找Key若是存在,說明數據已在緩存中,咱們只須要更新節點的值,並將節點放到鏈表頭部便可。若是不存在說明數據不在緩存中,則須要構造節點,並將其放置在頭部。在這個過程當中,若是發現緩存已滿,則須要淘汰掉鏈表尾部的數據並在HashMap中移除相應的Key。
  2. get(key)
    經過HashMap查找對應的節點,將其移動至頭部並返回。

代碼實現以下:數據結構

class LruCache<K, V>() {
    private data class Node<K, V>(
            var key: K? = null,
            var value: V? = null,
            var prev: Node<K, V>? = null,
            var next: Node<K, V>? = null
    )

    private val hashMap: HashMap<K, Node<K, V>> = hashMapOf()
    private var count = 0
    private var capacity = 8
    private val head: Node<K, V> = Node()
    private val tail: Node<K, V> = Node()

    init {
        head.next = tail
        tail.prev = head
    }

    constructor(capacity: Int) : this() {
        this.capacity = capacity
    }

    fun get(key: K): V? {
        val node = hashMap[key] ?: return null
        move(node)
        return node.value
    }

    fun put(key: K, value: V) {
        val node = hashMap[key]
        if (node == null) {
            val newNode = Node(key, value)
            add(newNode)
            hashMap[key] = newNode
            ++count
            if (count > capacity) {
                val deleteNode = delete()
                hashMap.remove(deleteNode.key)
                --count
            }
        } else {
            node.value = value
            move(node)
        }
    }

    private fun add(node: Node<K, V>) {
        node.prev = head
        node.next = head.next
        head.next!!.prev = node
        head.next = node
    }

    private fun remove(node: Node<K, V>) {
        val prev = node.prev!!
        val next = node.next!!
        prev.next = next
        next.prev = prev
    }

    private fun move(node: Node<K, V>) {
        remove(node)
        add(node)
    }

    private fun delete(): Node<K, V> {
        val node = tail.prev!!
        remove(node)
        return node
    }
}

而在實際使用中,咱們可使用LinkedHashMap實現,其內部就是使用雙向鏈表,咱們只需稍做修改便能使用。
在LinkedHashMap的構造參數(initialCapacity:Int, loadFactor:Float,accessOrder:Boolean)中,initialCapacity是HashMap的初始大小,loadFactor則是裝載因子,accessOrder=false表示基於插入順序,accessOrder=true表示基於訪問順序。
實現LRU的關鍵方法:ide

override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?): Boolean {
    return size > capacity
}

以上表示當LinkedHashMap大小超過咱們設定的大小時,移除鏈表首部的節點性能

class LruChche<K, V>(private val capacity: Int = 8) {
    private var hashMap: LinkedHashMap<K, V> = object : LinkedHashMap<K, V>
    (capacity / 0.75.toInt() + 1, 0.75f, true) {
        override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?): Boolean {
            return size > capacity
        }
    }

    fun get(key: K): V? = hashMap[key]
    fun put(key: K, value: V) {
        hashMap[key] = value
    }

}

結語

第一次據說LRU算法是在現代操做系統這本書中,但引發我深究的是Glide這個庫在自定義Model的時候,便有了一探究竟的想法,故整理資料寫下這些文字,一面是爲了加深本身的影響,另外一面也但願我所說的能讓你們更簡單的去理解LRU,一塊兒學習。學習

相關文章
相關標籤/搜索