java 最少使用(lru)置换算法_LRU 算法

a8e6c71b66b074c115381eb10a14369a.png
LRU(Least recently used),即最近最少使用算法,该算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

实现过程 1. 新数据将被插入到链表表头; 2. 访问缓存中的数据时,将该数据移动到链表表头; 3. 当链表满时,将链表尾部的数据丢弃。

0583630b6097e3785098311ee14c5271.png

页面置换算法 LRU 算法,是操作系统中一种典型的内存管理算法,常用于虚拟页式存储,这种页面置换算法的原理是,对于在内存中但又不用的数据块(内存块)叫做 LRU,操作系统会根据哪些数据属于 LRU 而将其移出内存,用于腾出空间来加载另外的数据。

看下面的一个例子:

一进程刚获得3个主存块的使用权,若该进程访问页面的次序是1、2、3、4、1、2、5、1、2、3、4、5。当采用LRU算法时,发生的缺页次数是 (10)

1d78baa8d98d63d66cd709eae8c388ef.png

微信小程序 微信小程序列表采用的就是 LRU 算法,当用户访问一个列表中没有的小程序时,会将该小程序置于表头;同样地,当用户访问一个列表中存在的小程序时,会将该小程序移至表头。

f0703ac5ec4aecd7964ff55c167bc2d2.png

cf8a2aabc3b9590e30ed619ab48d585d.png

LRU 的实现 首先,创建一个 Cache 基类:

public interface Cache<K, E> {
    boolean put(K key, E value);
    E get(Object o);
    E remove(Object o);
    void clear();
}

实现 LRU 算法:

public abstract class LruCache<K, E> implements Cache<K, E> {

    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * 最大容量,总是大于 {@link #size}
     */
    int capacity;

    /**
     * 当前元素总大小
     */
    int size;

    /**
     * 所有元素
     */
    LinkedHashMap<K, E> elements;

    /**
     * 获取单个元素的大小
     *
     * @param value
     * @return
     */
    protected int sizeOf(Object key, E value) {
        return 1;
    }

    protected Collection<E> values() {
        return elements.values();
    }

    public LruCache() {
        this(DEFAULT_INITIAL_CAPACITY);
    }

    public LruCache(int capacity) {
        if (capacity <= 0) {
            throw new IllegalArgumentException("capacity cannot be less than or equals 0.");
        }
        this.capacity = capacity;
        elements = new LinkedHashMap<>(0, DEFAULT_LOAD_FACTOR, true);
    }

    @Override
    public boolean put(K key, E value) {
        if (key == null || value == null) {
            throw new NullPointerException("key or value cant be null.");
        }

        synchronized (this) {
            E e = elements.put(key, value);
            if (e == null) {
                size += sizeOf(key, value);
            }
        }

        trimEle();
        return true;
    }

    protected void trimEle() {
        K key;
        E value;

        for (; ; ) {
            synchronized (this) {
                if (size < 0) {
                    throw new ArithmeticException("total size is less than 0.");
                }

                if (elements.isEmpty() || size <= capacity) {
                    break;
                }

                Map.Entry<K, E> deleting = elements.entrySet().iterator().next();
                if (deleting == null) {
                    break;
                }

                key = deleting.getKey();
                value = deleting.getValue();
                elements.remove(key);
                size -= sizeOf(key, value);
            }
        }
    }

    @Override
    public E get(Object key) {
        if (key == null) {
            throw new NullPointerException("key cant be null.");
        }

        synchronized (this) {
            return elements.get(key);
        }
    }

    @Override
    public E remove(Object key) {
        if (key == null) {
            throw new NullPointerException("key cant be null.");
        }

        synchronized (this) {
            E e = elements.remove(key);
            if (e != null) {
                size -= sizeOf(key, e);
            }
            return e;
        }
    }

    @Override
    public synchronized void clear() {
        elements.clear();
        size = 0;
    }

    /**
     * 重置缓存限制大小
     *
     * @param capacity
     */
    public void setupCapacity(int capacity) {
        if(capacity <= 0) {
            throw new IllegalArgumentException("capacity cannot be less than or equals 0.");
        } else {
            synchronized(this) {
                this.capacity = capacity;
            }
            trimEle();
        }
    }
}

一个具体的 LRU 缓存对象,比如使用该算法缓存 Integer 对象:

public class IntegerLruCache extends LruCache<Integer, Integer> {

    public IntegerLruCache() {
        super();
    }

    public IntegerLruCache(int capacity) {
        super(capacity);
    }

    @Override
    protected int sizeOf(Object key, Integer value) {
        return value;
    }

    /**
     * 求和
     *
     * @return
     */
    public int sum() {
        int sum = 0;
        Collection<Integer> values =  values();
        for (Integer num : values) {
            sum += num;
        }
        return sum;
    }

    public static void main(String[] args) {
        IntegerLruCache ilc = new IntegerLruCache(100);

        ilc.put(1, 25);
        ilc.put(2, 19);
        ilc.put(3, 22);

        System.out.println(ilc);

        ilc.get(1);

        System.out.println(ilc);
        System.out.println(" " + ilc.sum());

        ilc.put(4, 40);

        System.out.println(ilc);
        System.out.println(" " + ilc.sum());
    }
}
print:
[25, 19, 22]
[19, 22, 25]
 66
[22, 25, 40]
 87

LRU 作为一种有效地缓存淘汰算法,在 Android 中常用于缓存图片对象。

本文由 知我者乎 原创,未经许可,不得转载!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值