All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ttzero.excel.reader.FixSizeLRUCache Maven / Gradle / Ivy

/*
 * Copyright (c) 2017-2018, [email protected] All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ttzero.excel.reader;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.function.Consumer;

/**
 * Implemented by the LRU page elimination algorithm.
 * The time complexity of the push, get, and remove is O(1).
 *
 * @author guanquan.wang at 2018-10-31 16:06
 */
public class FixSizeLRUCache implements Cache {
    protected static class E implements Cache.Entry {
        private final K k;
        private V v;

        private E(K k, V v) {
            this.k = k;
            this.v = v;
        }

        @Override
        public K getKey() {
            return k;
        }
        @Override
        public V getValue() {
            return v;
        }
        @Override
        public String toString() {
            return k + ":" + v;
        }
    }

    private static class Node {
        private final V data;
        private Node prev, next;

        private Node(V data, Node prev, Node next) {
            this.data = data;
            this.prev = prev;
            this.next = next;
        }
    }

    /**
     * Double linked
     */
    private Node> first, last;

    /**
     * The elements limit
     */
    private final int limit;


    private final Map>> table;

    public static  FixSizeLRUCache create() {
        return new FixSizeLRUCache<>();
    }

    public static  FixSizeLRUCache create(int size) {
        return new FixSizeLRUCache<>(size);
    }

    /**
     * Create a fix size cache witch size is {@code 512}
     */
    private FixSizeLRUCache() {
        this(1 << 9);
    }

    private FixSizeLRUCache(int limit) {
        this.limit = limit;
        // Create double limit size
        table = new HashMap<>(limit);
    }

    /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this cache contains no mapping for the key.
     *
     * @param k the key whose associated value is to be returned
     * @return the value to which the specified key is mapped, or
     *      {@code null} if this cache contains no mapping for the key
     */
    @Override
    public V get(K k) {
        final Node> o = getNode(k);
        return o != null ? o.data.v : null;
    }

    /**
     * Associates the specified value with the specified key in this cache.
     * If the cache previously contained a mapping for
     * the key, the old value is replaced by the specified value.
     *
     * @param k key with which the specified value is to be associated
     *          the key must not be null
     * @param v value to be associated with the specified key
     */
    @Override
    public void put(K k, V v) {
        final Node> o = getNode(k);
        // Insert at header if not found
        if (o == null) {
            final Node> f = first;
            final Node> newNode = new Node<>(new E<>(k, v), null, f);
            first = newNode;
            if (f == null) last = newNode;
            else f.prev = newNode;

            table.put(k, newNode);

            if (table.size() > limit) remove();
        }
        // Replace the old value
        else {
            o.data.v = v;
        }
    }

    /**
     * Removes the mapping for a key from this cache if it is present
     * (optional operation).   More formally, if this cache contains a mapping
     * from key k to value v, that mapping
     * is removed.
     *
     * @param k key whose mapping is to be removed from the cache
     * @return the previous value associated with key, or
     *      null if there was no mapping for key.
     */
    @Override
    public V remove(K k) {
        final Node> o = table.get(k);
        // Not found
        if (o == null) return null;

        // Remove the keyword from the hash table to make them unsearchable
        table.remove(k);

        if (table.isEmpty()) {
            clear();
            return o.data.v;
        }

        final Node> prev = o.prev, next = o.next;

        // The first node
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            o.prev = null;
        }

        // The last node
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            o.next = null;
        }

        return o.data.v;
    }

    /**
     * Remove the last item
     *
     * @return the last item
     */
    protected E remove() {
        final Node> l = last;
        if (l == null) {
            return null;
        }
        final E data = l.data;

        last = last.prev;
        if (last != null) {
            last.next = null;
        }

        table.remove(data.k);
        if (last == null) first = null;
        return data;
    }

    /**
     * Removes all the mappings from this cache (optional operation).
     * The cache will be empty after this call returns.
     */
    @Override
    public void clear() {
        first = null;
        last = null;
        table.clear();
    }

    /**
     * Returns the number of key-value mappings in this cache.
     *
     * @return the number of key-value mappings in this cache
     */
    @Override
    public int size() {
        return table.size();
    }

    /**
     * An inner iterator
     */
    private class CacheIterator implements Iterator> {
        private Node> first;

        private CacheIterator(Node> first) {
            this.first = first;
        }

        @Override
        public boolean hasNext() {
            return first != null;
        }

        @Override
        public E next() {
            E e = first.data;
            first = first.next;
            return e;
        }
    }

    /**
     * Returns an iterator over elements of type {@code Cache.Entry}.
     *
     * @return an Iterator.
     */
    @Override
    public Iterator> iterator() {
        return new CacheIterator(first);
    }

    /**
     * Performs the given action for each element of the {@code Iterable}
     * until all elements have been processed or the action throws an
     * exception.  Unless otherwise specified by the implementing class,
     * actions are performed in the order of iteration (if an iteration order
     * is specified).  Exceptions thrown by the action are relayed to the
     * caller.
     *
     * 

The default implementation behaves as if: *

{@code
     *     for (T t : this)
     *         action.accept(t);
     * }
* * @param action The action to be performed for each element * @throws NullPointerException if the specified action is null */ @Override public void forEach(Consumer> action) { Node> f = first; for (; f != null; f = f.next) { action.accept(f.data); } } /** * Find and move to head * * @param k key * @return null if not found */ Node> getNode(K k) { final Node> o = table.get(k); // Not found if (o == null) return null; // Move node to head if (o != first) { if (o.next != null) { o.prev.next = o.next; o.next.prev = o.prev; } else { o.prev.next = null; last = o.prev; } first.prev = o; o.next = first; o.prev = null; first = o; } return o; } @Override public String toString() { Node> f = first; if (f != null) { StringBuilder buf = new StringBuilder(); buf.append(f.data.k).append(':').append(f.data.v); f = f.next; for (int i = 1, n = 1000; i < n && f != null; f = f.next, i++) { buf.append("=>").append(f.data.k).append(':').append(f.data.v); } return buf.toString(); } else return ""; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy