LRU算法及Apache LRUMap源码实例解析

目录
  • 1. 什么是LRU
    • 1.1 自定义实现LRU的要求
    • 1.2 Apache LRUMap示例
      • 1.2.1 pom依赖
      • 1.2.2 demo
  • 2. 源码解析
    • 2.1 设计
    • 2.2 数据结构
    • 2.3 方法解析put get remove
      • 2.3.1 get方法
      • 2.3.2 remove方法
      • 2.3.3 put方法
  • 3. 总结

1. 什么是LRU

LRU(least recently used) : 最近最少使用

LRU就是一种经典的算法,在容器中,对元素定义一个最后使用时间,当新的元素写入的时候,如果容器已满,则淘汰最近最少使用的元素,把新的元素写入。

1.1 自定义实现LRU的要求

比如redis,如何自己实现简易版的redis缓存。

那么我们需要一种数据结构,支持set和get操作。

1) get操作时间复杂度O(1);

2)需要支持RLU算法,空间不足时,需要将使用最少的元素移除,为新元素让空间;

3)时间失效remove(这个先不谈,比较麻烦)。

1.2 Apache LRUMap示例

1.2.1 pom依赖

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.2</version>
        </dependency>

1.2.2 demo

        LRUMap<String, String> map = new LRUMap<>(3);
        map.put("1", "1");
        map.put("2", "2");
        map.put("3", "3");

        map.get("2");

        System.out.println("---------------------------------");
        map.forEach((k,v)->
            System.out.println(k+"\t"+v)
        );

        map.put("4", "4");
        map.put("5", "5");

        System.out.println("---------------------------------");
        map.forEach((k,v)->
                System.out.println(k+"\t"+v)
        );

        map.put("6", "6");

        System.out.println("---------------------------------");
        map.forEach((k,v)->
                System.out.println(k+"\t"+v)
        );

结果如下:

---------------------------------

1 1

3 3

2 2

---------------------------------

2 2

4 4

5 5

---------------------------------

4 4

5 5

6 6

可以看出在get("2"),2的位置挪后,然后移除的顺序就延后。

容量不足时,总是移除,使用最少的,时间最远的。

2. 源码解析

2.1 设计

public class LRUMap<K, V>
        extends AbstractLinkedMap<K, V> implements BoundedMap<K, V>, Serializable, Cloneable {

进一步查看AbstractLinkedMap,AbstractHashedMap

public abstract class AbstractLinkedMap<K, V> extends AbstractHashedMap<K, V> implements OrderedMap<K, V> {
public class AbstractHashedMap<K, V> extends AbstractMap<K, V> implements IterableMap<K, V> {

本质是自定义AbstractMap

我们看看HashMap LinkedHashMap

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

可以看出AbstractMap,AbstractHashedMap,LRUMap的本质其实也是HashMap。

2.2 数据结构

protected static final int DEFAULT_MAX_SIZE = 100;

public LRUMap() {
        this(DEFAULT_MAX_SIZE, DEFAULT_LOAD_FACTOR, false);
}

可以看出默认初始化容量100,最大容量也是100.

进一步跟踪

public LRUMap(final int maxSize, final float loadFactor, final boolean scanUntilRemovable) {
        this(maxSize, maxSize, loadFactor, scanUntilRemovable);
}

/**
     * Constructs a new, empty map with the specified max / initial capacity and load factor.
     *
     * @param maxSize  the maximum size of the map
     * @param initialSize  the initial size of the map
     * @param loadFactor  the load factor
     * @param scanUntilRemovable  scan until a removeable entry is found, default false
     * @throws IllegalArgumentException if the maximum size is less than one
     * @throws IllegalArgumentException if the initial size is negative or larger than the maximum size
     * @throws IllegalArgumentException if the load factor is less than zero
     * @since 4.1
     */
    public LRUMap(final int maxSize,
                  final int initialSize,
                  final float loadFactor,
                  final boolean scanUntilRemovable) {

        super(initialSize, loadFactor);
        if (maxSize < 1) {
            throw new IllegalArgumentException("LRUMap max size must be greater than 0");
        }
        if (initialSize > maxSize) {
            throw new IllegalArgumentException("LRUMap initial size must not be greather than max size");
        }
        this.maxSize = maxSize;
        this.scanUntilRemovable = scanUntilRemovable;
    }

跟踪super(initialSize, loadFactor);

public abstract class AbstractLinkedMap<K, V> extends AbstractHashedMap<K, V> implements OrderedMap<K, V> {

    protected AbstractLinkedMap(final int initialCapacity, final float loadFactor) {
        super(initialCapacity, loadFactor);
    }
//又super,再上一层追踪

public class AbstractHashedMap<K, V> extends AbstractMap<K, V> implements IterableMap<K, V> {
    //定义一些基本初始化数据
    /** The default capacity to use */
    protected static final int DEFAULT_CAPACITY = 16;
    /** The default threshold to use */
    protected static final int DEFAULT_THRESHOLD = 12;
    /** The default load factor to use */
    protected static final float DEFAULT_LOAD_FACTOR = 0.75f;
    /** The maximum capacity allowed */
    protected static final int MAXIMUM_CAPACITY = 1 << 30;

    /** Load factor, normally 0.75 */
    transient float loadFactor;
    /** The size of the map */
    transient int size;
    /** Map entries */
    transient HashEntry<K, V>[] data;
    /** Size at which to rehash */
    transient int threshold;
    /** Modification count for iterators */
    transient int modCount;
    /** Entry set */
    transient EntrySet<K, V> entrySet;
    /** Key set */
    transient KeySet<K> keySet;
    /** Values */
    transient Values<V> values;

    protected AbstractHashedMap(int initialCapacity, final float loadFactor) {
        super();
        if (initialCapacity < 0) {
            throw new IllegalArgumentException("Initial capacity must be a non negative number");
        }
        if (loadFactor <= 0.0f || Float.isNaN(loadFactor)) {
            throw new IllegalArgumentException("Load factor must be greater than 0");
        }
        this.loadFactor = loadFactor;
        initialCapacity = calculateNewCapacity(initialCapacity);
        this.threshold = calculateThreshold(initialCapacity, loadFactor);
        this.data = new HashEntry[initialCapacity];
        init();
    }

    /**
     * Initialise subclasses during construction, cloning or deserialization.
     */
    protected void init() {
        //没有任何逻辑,仅用于子类构造
    }

DEFAULT_LOAD_FACTOR = 0.75f; 负载因子0.75

可以看出LRUMap的本质,HashEntry数组。

上面的init方法没有实现逻辑,但是在他的子类中AbstractLinkedMap有相关的定义。

    /** Header in the linked list */
    transient LinkEntry<K, V> header;

    /**
     * Creates an entry to store the data.
     * <p>
     * This implementation creates a new LinkEntry instance.
     *
     * @param next  the next entry in sequence
     * @param hashCode  the hash code to use
     * @param key  the key to store
     * @param value  the value to store
     * @return the newly created entry
     */
    @Override
    protected LinkEntry<K, V> createEntry(final HashEntry<K, V> next, final int hashCode, final K key, final V value) {
        return new LinkEntry<>(next, hashCode, convertKey(key), value);
    }

    protected static class LinkEntry<K, V> extends HashEntry<K, V> {
        /** The entry before this one in the order */
        protected LinkEntry<K, V> before;
        /** The entry after this one in the order */
        protected LinkEntry<K, V> after;

        /**
         * Constructs a new entry.
         *
         * @param next  the next entry in the hash bucket sequence
         * @param hashCode  the hash code
         * @param key  the key
         * @param value  the value
         */
        protected LinkEntry(final HashEntry<K, V> next, final int hashCode, final Object key, final V value) {
            super(next, hashCode, key, value);
        }
    }

    /**
     * Initialise this subclass during construction.
     * <p>
     * NOTE: As from v3.2 this method calls
     * {@link #createEntry(HashEntry, int, Object, Object)} to create
     * the map entry object.
     */
    @Override
    protected void init() {
        header = createEntry(null, -1, null, null);
        header.before = header.after = header;
    }

这个很关键。可以看出LRUMap是持有LinkEntry header,的双链表结构,初始header为null,前后节点都是自身。将HashEntry转成LinkEntry。

解析HashEntry

transient HashEntry<K, V>[] data;
//构造初始化
this.data = new HashEntry[initialCapacity];

再跟踪

 protected static class HashEntry<K, V> implements Map.Entry<K, V>, KeyValue<K, V> {
        /** The next entry in the hash chain */
        protected HashEntry<K, V> next;
        /** The hash code of the key */
        protected int hashCode;
        /** The key */
        protected Object key;
        /** The value */
        protected Object value;

key,value,next单链表。

public int hashCode() {
            return (getKey() == null ? 0 : getKey().hashCode()) ^
                   (getValue() == null ? 0 : getValue().hashCode());
        }

hashCode方法可以看出是key的hash与value的hash按位^运算。

在此我们看透LRU的本质了,数组+单链表。同时是持有头结点的双链表结构(怎么看就是LinkedHashMap的结构,只是有尾节点)。

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
{
    /**
     * The head (eldest) of the doubly linked list.
     */
    transient LinkedHashMap.Entry<K,V> head;

    /**
     * The tail (youngest) of the doubly linked list.
     */
    transient LinkedHashMap.Entry<K,V> tail;

那么LRUMap是如何实现LRU算法的?

2.3 方法解析put get remove

2.3.1 get方法

public V get(final Object key) {
        return get(key, true);
}

public V get(final Object key, final boolean updateToMRU) {
        final LinkEntry<K, V> entry = getEntry(key);
        if (entry == null) {
            return null;
        }
        if (updateToMRU) {
            moveToMRU(entry);
        }
        return entry.getValue();
}

//父类方法获取值entry
protected HashEntry<K, V> getEntry(Object key) {
        key = convertKey(key);
        final int hashCode = hash(key);
        HashEntry<K, V> entry = data[hashIndex(hashCode, data.length)]; // no local for hash index
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
                return entry;
            }
            entry = entry.next;
        }
        return null;
}

下面看不一样的moveToMRU(entry);

/**
     * Moves an entry to the MRU position at the end of the list.
     * <p>
     * This implementation moves the updated entry to the end of the list.
     *
     * @param entry  the entry to update
     */
    protected void moveToMRU(final LinkEntry<K, V> entry) {
        if (entry.after != header) {
            modCount++;
            // remove
            if(entry.before == null) {
                throw new IllegalStateException("Entry.before is null." +
                    " Please check that your keys are immutable, and that you have used synchronization properly." +
                    " If so, then please report this to dev@commons.apache.org as a bug.");
            }
            entry.before.after = entry.after;
            entry.after.before = entry.before;
            // add first
            entry.after = header;
            entry.before = header.before;
            header.before.after = entry;
            header.before = entry;
        } else if (entry == header) {
            throw new IllegalStateException("Can't move header to MRU" +
                " (please report this to dev@commons.apache.org)");
        }
    }

看出LRU的一个本质,每次get方法拨动指针,将get的元素移动到header的前一个位置。

2.3.2 remove方法

remove方法使用的父类的方法

    /**
     * Removes the specified mapping from this map.
     *
     * @param key  the mapping to remove
     * @return the value mapped to the removed key, null if key not in map
     */
    @Override
    public V remove(Object key) {
        key = convertKey(key);
        final int hashCode = hash(key);
        final int index = hashIndex(hashCode, data.length);
        HashEntry<K, V> entry = data[index];
        HashEntry<K, V> previous = null;
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
                final V oldValue = entry.getValue();
                removeMapping(entry, index, previous);
                return oldValue;
            }
            previous = entry;
            entry = entry.next;
        }
        return null;
    }

    /**
     * Removes a mapping from the map.
     * <p>
     * This implementation calls <code>removeEntry()</code> and <code>destroyEntry()</code>.
     * It also handles changes to <code>modCount</code> and <code>size</code>.
     * Subclasses could override to fully control removals from the map.
     *
     * @param entry  the entry to remove
     * @param hashIndex  the index into the data structure
     * @param previous  the previous entry in the chain
     */
    protected void removeMapping(final HashEntry<K, V> entry, final int hashIndex, final HashEntry<K, V> previous) {
        modCount++;
        removeEntry(entry, hashIndex, previous);
        size--;
        destroyEntry(entry);
    }

    protected void removeEntry(final HashEntry<K, V> entry, final int hashIndex, final HashEntry<K, V> previous) {
        if (previous == null) {
            data[hashIndex] = entry.next;
        } else {
            previous.next = entry.next;
        }
    }

    protected void destroyEntry(final HashEntry<K, V> entry) {
        entry.next = null;
        entry.key = null;
        entry.value = null;
    }

这里并没有移除header双链表的数据。

2.3.3 put方法

    /**
     * Puts a key-value mapping into this map.
     *
     * @param key  the key to add
     * @param value  the value to add
     * @return the value previously mapped to this key, null if none
     */
    @Override
    public V put(final K key, final V value) {
        final Object convertedKey = convertKey(key);
        final int hashCode = hash(convertedKey);
        final int index = hashIndex(hashCode, data.length);
        HashEntry<K, V> entry = data[index];
        //仅在元素存在才循环,更新updateEntry,header前一个位置
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(convertedKey, entry.key)) {
                final V oldValue = entry.getValue();
                updateEntry(entry, value);
                return oldValue;
            }
            entry = entry.next;
        }

        addMapping(index, hashCode, key, value);
        return null;
    } 

updateEntry(entry, value);

    /**
     * Updates an existing key-value mapping.
     * <p>
     * This implementation moves the updated entry to the end of the list
     * using {@link #moveToMRU(AbstractLinkedMap.LinkEntry)}.
     *
     * @param entry  the entry to update
     * @param newValue  the new value to store
     */
    @Override
    protected void updateEntry(final HashEntry<K, V> entry, final V newValue) {
        moveToMRU((LinkEntry<K, V>) entry);  // handles modCount
        entry.setValue(newValue);
    }

 moveToMRU((LinkEntry<K, V>) entry);  // handles modCount

上面get方法有讲,更新了链表的指针,新添加的元素在双链表的header前一个位置,仅在元素存在的时候,while循环才生效。

 那么新增的元素呢?

下面看重点 addMapping(index, hashCode, key, value); 这句代码定义了,容量满了的处理策略。

    /**
     * Adds a new key-value mapping into this map.
     * <p>
     * This implementation checks the LRU size and determines whether to
     * discard an entry or not using {@link #removeLRU(AbstractLinkedMap.LinkEntry)}.
     * <p>
     * From Commons Collections 3.1 this method uses {@link #isFull()} rather
     * than accessing <code>size</code> and <code>maxSize</code> directly.
     * It also handles the scanUntilRemovable functionality.
     *
     * @param hashIndex  the index into the data array to store at
     * @param hashCode  the hash code of the key to add
     * @param key  the key to add
     * @param value  the value to add
     */
    @Override
    protected void addMapping(final int hashIndex, final int hashCode, final K key, final V value) {
        //容量是否已满
        if (isFull()) {
            LinkEntry<K, V> reuse = header.after;
            boolean removeLRUEntry = false;
            //默认是false
            if (scanUntilRemovable) {
                //这里不知道干啥,难道是以后扩展?
                while (reuse != header && reuse != null) {
                    //removeLRU一定返回true,很奇怪,估计以后扩展用
                    if (removeLRU(reuse)) {
                        removeLRUEntry = true;
                        break;
                    }
                    reuse = reuse.after;
                }
                if (reuse == null) {
                    throw new IllegalStateException(
                        "Entry.after=null, header.after" + header.after + " header.before" + header.before +
                        " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize +
                        " Please check that your keys are immutable, and that you have used synchronization properly." +
                        " If so, then please report this to dev@commons.apache.org as a bug.");
                }
            } else {
                //一定返回true
                removeLRUEntry = removeLRU(reuse);
            }

            if (removeLRUEntry) {
                if (reuse == null) {
                    throw new IllegalStateException(
                        "reuse=null, header.after=" + header.after + " header.before" + header.before +
                        " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize +
                        " Please check that your keys are immutable, and that you have used synchronization properly." +
                        " If so, then please report this to dev@commons.apache.org as a bug.");
                }
                reuseMapping(reuse, hashIndex, hashCode, key, value);
            } else {
                super.addMapping(hashIndex, hashCode, key, value);
            }
        } else {
            super.addMapping(hashIndex, hashCode, key, value);
        }
    }

    protected boolean removeLRU(final LinkEntry<K, V> entry) {
        return true;
    }

先判断容量

public boolean isFull() {
        return size >= maxSize;
}

未满就直接添加

super.addMapping(hashIndex, hashCode, key, value);

    protected void addMapping(final int hashIndex, final int hashCode, final K key, final V value) {
        modCount++;
        final HashEntry<K, V> entry = createEntry(data[hashIndex], hashCode, key, value);
        addEntry(entry, hashIndex);
        size++;
        checkCapacity();
    }

//这里调用了AbstractLinkedMap的方法 

addEntry(entry, hashIndex);

    /**
     * Adds an entry into this map, maintaining insertion order.
     * <p>
     * This implementation adds the entry to the data storage table and
     * to the end of the linked list.
     *
     * @param entry  the entry to add
     * @param hashIndex  the index into the data array to store at
     */
    @Override
    protected void addEntry(final HashEntry<K, V> entry, final int hashIndex) {
        final LinkEntry<K, V> link = (LinkEntry<K, V>) entry;
        link.after  = header;
        link.before = header.before;
        header.before.after = link;
        header.before = link;
        data[hashIndex] = link;
    }

 放在header的前一个位置,最早的元素链接到header。

双向环回链表。

 如果容量满了,执行LRU算法 reuseMapping(reuse, hashIndex, hashCode, key, value);

    /**
     * Reuses an entry by removing it and moving it to a new place in the map.
     * <p>
     * This method uses {@link #removeEntry}, {@link #reuseEntry} and {@link #addEntry}.
     *
     * @param entry  the entry to reuse
     * @param hashIndex  the index into the data array to store at
     * @param hashCode  the hash code of the key to add
     * @param key  the key to add
     * @param value  the value to add
     */
    protected void reuseMapping(final LinkEntry<K, V> entry, final int hashIndex, final int hashCode,
                                final K key, final V value) {
        // find the entry before the entry specified in the hash table
        // remember that the parameters (except the first) refer to the new entry,
        // not the old one
        try {
            //要干掉的元素下标
            final int removeIndex = hashIndex(entry.hashCode, data.length);
            final HashEntry<K, V>[] tmp = data;  // may protect against some sync issues
            HashEntry<K, V> loop = tmp[removeIndex];
            HashEntry<K, V> previous = null;
            //避免已经被删除
            while (loop != entry && loop != null) {
                previous = loop;
                loop = loop.next;
            }
            //如果被其他线程删除,抛异常
            if (loop == null) {
                throw new IllegalStateException(
                    "Entry.next=null, data[removeIndex]=" + data[removeIndex] + " previous=" + previous +
                    " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize +
                    " Please check that your keys are immutable, and that you have used synchronization properly." +
                    " If so, then please report this to dev@commons.apache.org as a bug.");
            }

            // reuse the entry
            modCount++;
            //双链表移除旧元素,AbstractHashedMap移除旧元素
            removeEntry(entry, removeIndex, previous);
            //复用移除的对象,减少创建对象和GC;增加AbstractHashedMap单链表next指向
            reuseEntry(entry, hashIndex, hashCode, key, value);
            //复用的元素加AbstractLinkedMap双链表和AbstractHashedMap单链表
            addEntry(entry, hashIndex);
        } catch (final NullPointerException ex) {
            throw new IllegalStateException(
                    "NPE, entry=" + entry + " entryIsHeader=" + (entry==header) +
                    " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize +
                    " Please check that your keys are immutable, and that you have used synchronization properly." +
                    " If so, then please report this to dev@commons.apache.org as a bug.");
        }
    }
    /**
     * Removes an entry from the map and the linked list.
     * <p>
     * This implementation removes the entry from the linked list chain, then
     * calls the superclass implementation.
     *
     * @param entry  the entry to remove
     * @param hashIndex  the index into the data structure
     * @param previous  the previous entry in the chain
     */
    @Override
    protected void removeEntry(final HashEntry<K, V> entry, final int hashIndex, final HashEntry<K, V> previous) {
        final LinkEntry<K, V> link = (LinkEntry<K, V>) entry;
        link.before.after = link.after;
        link.after.before = link.before;
        link.after = null;
        link.before = null;
        super.removeEntry(entry, hashIndex, previous);
    }

    /**
     * Removes an entry from the chain stored in a particular index.
     * <p>
     * This implementation removes the entry from the data storage table.
     * The size is not updated.
     * Subclasses could override to handle changes to the map.
     *
     * @param entry  the entry to remove
     * @param hashIndex  the index into the data structure
     * @param previous  the previous entry in the chain
     */
    protected void removeEntry(final HashEntry<K, V> entry, final int hashIndex, final HashEntry<K, V> previous) {
        if (previous == null) {
            data[hashIndex] = entry.next;
        } else {
            previous.next = entry.next;
        }
    }

    /**
     * Reuses an existing key-value mapping, storing completely new data.
     * <p>
     * This implementation sets all the data fields on the entry.
     * Subclasses could populate additional entry fields.
     *
     * @param entry  the entry to update, not null
     * @param hashIndex  the index in the data array
     * @param hashCode  the hash code of the key to add
     * @param key  the key to add
     * @param value  the value to add
     */
    protected void reuseEntry(final HashEntry<K, V> entry, final int hashIndex, final int hashCode,
                              final K key, final V value) {
        entry.next = data[hashIndex];
        entry.hashCode = hashCode;
        entry.key = key;
        entry.value = value;
    }

    /**
     * Adds an entry into this map, maintaining insertion order.
     * <p>
     * This implementation adds the entry to the data storage table and
     * to the end of the linked list.
     *
     * @param entry  the entry to add
     * @param hashIndex  the index into the data array to store at
     */
    @Override
    protected void addEntry(final HashEntry<K, V> entry, final int hashIndex) {
        final LinkEntry<K, V> link = (LinkEntry<K, V>) entry;
        link.after  = header;
        link.before = header.before;
        header.before.after = link;
        header.before = link;
        data[hashIndex] = link;
    }

3. 总结

LRU的本质了,数组+单链表。同时是持有头结点的环回双链表结构

LRU最新使用的元素放在双链表的header的前一个位置,如果,新增元素容量已满就会移除header的后一个元素。

到此这篇关于LRU算法及Apache LRUMap源码实例解析的文章就介绍到这了,更多相关LRU算法及Apache LRUMap源码内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java LRU算法介绍与用法示例

    本文实例讲述了java LRU算法介绍与用法.分享给大家供大家参考,具体如下: 1.前言 在用户使用联网的软件的时候,总会从网络上获取数据,当在一段时间内要多次使用同一个数据的时候,用户不可能每次用的时候都去联网进行请求,既浪费时间又浪费网络 这时就可以将用户请求过的数据进行保存,但不是任意数据都进行保存,这样会造成内存浪费的.LRU算法的思想就可以运用了. 2.LRU简介 LRU是Least Recently Used 近期最少使用算法,它就可以将长时间没有被利用的数据进行删除. LRU在人们

  • Java 手写LRU缓存淘汰算法

    概述 LRU 算法全称为 Least Recently Used 是一种常见的页面缓存淘汰算法,当缓存空间达到达到预设空间的情况下会删除那些最久没有被使用的数据 . 常见的页面缓存淘汰算法主要有一下几种: LRU 最近最久未使用 FIFO 先进先出置换算法 类似队列 OPT 最佳置换算法 (理想中存在的) NRU Clock 置换算法 LFU 最少使用置换算法 PBA 页面缓冲算法 LRU 的原理 LRU 算法的设计原理其实就是计算机的 局部性原理(这个 局部性原理 包含了 空间局部性 和 时间

  • JAVA实现LRU算法的参考示例

    LRU简介 LRU是Least Recently Used 近期最少使用算法,它就可以将长时间没有被利用的数据进行删除. 实现 最近面了阿里的外包吧,居然也要在线敲代码了,那叫一个紧张啊.题目就是实现一个LRU算法的缓存.外包居然要求也这么高了,哎.还好,LRU是我大学老师布置的一道题目,当然我用C语言实现的,算法原理那是一清二楚,可是面试的时候就脑子一片空白了.好在,边敲代码,边思考,就慢慢想起来了,下面是我的代码.仅供参考 /** * 设计和构建一个"最近最少使用"LRU 缓存,该

  • LRU算法及Apache LRUMap源码实例解析

    目录 1. 什么是LRU 1.1 自定义实现LRU的要求 1.2 Apache LRUMap示例 1.2.1 pom依赖 1.2.2 demo 2. 源码解析 2.1 设计 2.2 数据结构 2.3 方法解析put get remove 2.3.1 get方法 2.3.2 remove方法 2.3.3 put方法 3. 总结 1. 什么是LRU LRU(least recently used) : 最近最少使用 LRU就是一种经典的算法,在容器中,对元素定义一个最后使用时间,当新的元素写入的时候

  • vue Proxy数据代理进行校验部分源码实例解析

    目录 initProxy 触发代理 数据过滤 总结 initProxy 数据拦截的思想除了为构建响应式系统准备,它也可以为数据进行筛选过滤,我们接着往下看初始化的代码,在合并选项后,vue接下来会为vm实例设置一层代理,这层代理可以为vue在模板渲染时进行一层数据筛选 Vue.prototype._init = function(options) { // 选项合并 ... { // 对vm实例进行一层代理 initProxy(vm); } ... } initProxy // 代理函数 var

  • PHP中实现汉字转区位码应用源码实例解析

    复制代码 代码如下: <?php global $PHP_SELF; //echo $PHP_SELF; $t1=$_POST['textfield1']; $t2=$_POST['textfield2']; $t3=$_POST['textfield3']; $t4=$_POST['textfield4']; // 汉字--区位码 if($t1!=""){ $t2= sprintf("%02d%02d",ord($t1[0])-160,ord($t1[1])

  • spring cloud openfeign 源码实例解析

    一.读取注解信息 入口 import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class

  • java中创建写入文件的6种方式详解与源码实例

    在java中有很多的方法可以创建文件写文件,你是否真的认真的总结过?下面笔者就帮大家总结一下java中创建文件的五种方法. Files.newBufferedWriter(Java 8) Files.write(Java 7 推荐) PrintWriter File.createNewFile FileOutputStream.write(byte[] b) 管道流 实际上不只这5种,通过管道流的排列组合,其实有更多种,但是笔者总结的这五种可以说是最常用及最佳实践,前提小知识 以前我在写技术文章

  • apache的源码安装详细过程全纪录

    最近要开始学习nagios监控方面的知识了,但是nagios与apache结合的比较紧密,所以本篇文章就先把apache的源码安装学习下. 我们现在分以下步骤进行安装apache: 1. 安装编译环境 2. 卸载原有apache 3. 下载解压源码包 4. 安装apache 5. 测试apache 6. 查看apache安装生成的目录 7. 查看apache的配置文件 8. apache加入系统服务 一.安装编译环境 在安装apache之前,我们需要安装编译apache时所需要的相关软件包,如下

  • 微信小程序 自动登陆PHP源码实例(源码下载)

    微信小程序 自动登陆PHP源码实例 app.js 初始化APP自动登陆 您也可以在任何地方进行用户登陆验证 用法:首先在js文件中定义 var app = getApp(); app.getUserDataToken(); App({ onLaunch: function () { /*初始化APP自动登陆 * 您也可以在任何地方进行用户登陆验证 *用法:首先在js文件中定义 var app = getApp(); app.getUserDataToken(); */ this.getUserD

  • java向文件中追加内容与读写文件内容源码实例代码

    java向文件中追加内容与读写文件内容源码实例代码 向文件尾加入内容有多种方法,常见的方法有两种: RandomAccessFile类可以实现随机访问文件的功能,可以以读写方式打开文件夹的输出流 public void seek(long pos)可以将读写指针移到文件尾,参数Pos表示从文件开头以字节为单位测量的偏移位置,在该位置文件指针. public void write(int pos)将数据写到读写指针后面,完成文件的追加.参数pos表示要写入的Byte 通过FileWrite打开文件

  • 深入解析Vue源码实例挂载与编译流程实现思路详解

    在正文开始之前,先了解vue基于源码构建的两个版本,一个是 runtime only ,另一个是 runtime加compiler 的版本,两个版本的主要区别在于后者的源码包括了一个编译器. 什么是编译器,百度百科上面的解释是 简单讲,编译器就是将"一种语言(通常为高级语言)"翻译为"另一种语言(通常为低级语言)"的程序.一个现代编译器的主要工作流程:源代码 (source code) → 预处理器 (preprocessor) → 编译器 (compiler) →

  • Android开发实现TextView超链接5种方式源码实例

    Android实现TextView超链接一共有五种方式:推荐第四种.第五种 1. 直接在xml文件中配置autoLink属性(简单易用,效果单一) autoLink属性一共有六个值,分别是none(正常),web(将文本识别为一个网址),phone(将文本识别为一个电话号码),mail(将文本识别为一个邮件地址),map(这个,呃,该怎么表述呢?会打开地图应用),all(根据文本自动识别).一般情况下我们设置为all即可,我们看看,这个时候它就会自动将TextView中的电话号码.邮件地址.网页

随机推荐