LFU cache in Kotlin

2 min read 29-09-2024
LFU cache in Kotlin


What is LFU Cache?

An LFU (Least Frequently Used) cache is a type of cache eviction algorithm that removes the least frequently used items first when the cache reaches its limit. This approach is particularly useful in scenarios where certain items are accessed more often than others, ensuring that the most frequently accessed data remains readily available.

Problem Scenario

Consider an application that needs to efficiently manage a limited amount of memory for cached items. When the memory limit is reached, you want to ensure that the least frequently accessed items are removed to make room for new data. In Kotlin, implementing an LFU cache can help solve this problem effectively.

Example Code

Here's a simple implementation of LFU Cache in Kotlin:

class LFUCache(private val capacity: Int) {

    private val cache: MutableMap<Int, Pair<Int, Int>> = mutableMapOf() // Key -> (Value, Frequency)
    private val frequencyMap: MutableMap<Int, MutableList<Int>> = mutableMapOf() // Frequency -> Keys
    private var minFrequency = 0

    fun get(key: Int): Int {
        if (!cache.containsKey(key)) return -1

        // Update frequency
        val (value, frequency) = cache[key]!!
        cache[key] = Pair(value, frequency + 1)
        frequencyMap[frequency]?.remove(key)

        if (frequencyMap[frequency]?.isEmpty() == true) {
            frequencyMap.remove(frequency)
            if (minFrequency == frequency) {
                minFrequency++
            }
        }

        frequencyMap.computeIfAbsent(frequency + 1) { mutableListOf() }.add(key)
        return value
    }

    fun put(key: Int, value: Int) {
        if (capacity <= 0) return

        if (cache.containsKey(key)) {
            cache[key] = Pair(value, cache[key]!!.second)
            get(key) // Update frequency
            return
        }

        if (cache.size >= capacity) {
            val keys = frequencyMap[minFrequency] ?: return
            val evictKey = keys.removeAt(0)
            cache.remove(evictKey)

            if (keys.isEmpty()) {
                frequencyMap.remove(minFrequency)
            }
        }

        cache[key] = Pair(value, 1)
        minFrequency = 1
        frequencyMap.computeIfAbsent(1) { mutableListOf() }.add(key)
    }
}

Analysis of the LFU Cache Implementation

How It Works

  1. Data Structures:

    • A cache map (cache) stores the key-value pairs along with their access frequency.
    • A frequency map (frequencyMap) keeps track of which keys correspond to each access frequency.
  2. Get Method:

    • When a key is accessed, it checks if the key exists in the cache.
    • If it does, it updates the frequency and moves the key to the appropriate frequency bucket.
  3. Put Method:

    • When adding a key-value pair, the method first checks if the cache size exceeds its capacity.
    • If so, it evicts the least frequently used item.
    • Finally, it adds the new item to the cache and initializes its frequency.

Practical Examples of LFU Cache Usage

  • Web Browsers: LFU caches can help keep the most visited pages readily available for faster access, improving user experience.
  • Mobile Applications: For apps that maintain user preferences or session data, LFU caches can store the most frequently used data, optimizing performance.
  • Database Query Caching: In scenarios where certain database queries are executed more frequently, an LFU cache can help cache those results, reducing load times.

Conclusion

Implementing an LFU Cache in Kotlin can significantly optimize memory usage and improve access times for frequently used items. This algorithm suits scenarios where data access patterns vary significantly.

Useful Resources

By understanding the LFU Cache implementation and its benefits, you can enhance the performance of your applications and manage memory effectively.