将缓存对象转换为HashMap

时间:2019-03-05 20:47:55

标签: java caching hashmap lru

我有一个内存缓存类,用于存储Product对象和所售物品的数量。

public class MemoryCache<K, V> {

    private long timeToLive;
    private LRUMap lruMap;

    /**
     * custom class that stores the cache value
     * and the timestamp for the last access
     */
    protected class CacheObject {

        public long lastAccessed = System.currentTimeMillis();
        public V value;

        protected CacheObject(V value) {
            this.value = value;
        }
    }

    /**
     * @param timeToLive    this is the permitted period of time for an object to live since
     *                      they are last accessed.
     *
     *                      <p>
     * @param timerInterval For the expiration of items use the timestamp of the last access
     *                      and in a separate thread remove the items when the time to live
     *                      limit is reached. This is nice for reducing memory pressure for
     *                      applications that have long idle time in between accessing the
     *                      cached objects. We have disabled the cleanup for this case scenario
     *
     *                      <p>
     * @param maxItems      Cache will keep most recently used items if we will try to add more
     *                      items then max specified. The Apache common collections have an LRUMap,
     *                      which, removes the least used entries from a fixed size map
     */
    public MemoryCache(long timeToLive, final long timerInterval, int maxItems) {

        this.timeToLive = timeToLive * 1000;

        lruMap = new LRUMap(maxItems);

        if (this.timeToLive > 0 && timerInterval > 0) {

            Thread t = new Thread(new Runnable() {

                public void run() {

                    while (true) {
                        try {
                            Thread.sleep(timerInterval * 1000);
                        } catch (InterruptedException ex) {
                        }

                        /*
                         * clean the objects from the cache that has reached
                         * the timeToLive period after the last access.
                         * */
                        cleanup();
                    }
                }
            });

            t.setDaemon(true);
            t.start();
        }
    }


    /**
     * insert a new key and value inside the cache memory
     *
     * @param key
     * @param value
     */
    public void put(K key, V value) {

        synchronized (lruMap) {

            if (key == null) {
                return;
            }

            /**
             * we have reached the max. size of items decided for the cache
             * and hence, we are not allowed to add more items for now. We
             * will need for the cache cleaning to add further items.
             */
            if (lruMap.isFull()) {
                return;
            }

            lruMap.put(key, new CacheObject(value));
        }
    }


    /**
     * retrieve the cache object from the memory using the key
     *
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public V get(K key) {

        synchronized (lruMap) {

            MapIterator iterator = lruMap.mapIterator();

            K k = null;
            V v = null;

            CacheObject o = null;

            while (iterator.hasNext()) {

                k = (K) iterator.next();
                v = (V) iterator.getValue();

                Product product = (Product) k;
                Product product1 = (Product) key;

                if (product.getProductId().equalsIgnoreCase(product1.getProductId())) {
                    o = (CacheObject) v;
                }
            }

            if (o == null) {
                return null;
            } else {
                o.lastAccessed = System.currentTimeMillis();
                return o.value;
            }
        }
    }

    /**
     * remove a cache object from the memory using the key
     *
     * @param key
     */
    public void remove(K key) {

        synchronized (lruMap) {
            lruMap.remove(key);
        }
    }

    /**
     * find the size of the memory cache
     *
     * @return size of the cache
     */
    public int size() {

        synchronized (lruMap) {
            return lruMap.size();
        }
    }


    /**
     * we will look after the cache objects with a certain time interval
     * that has stayed in the memory inactively more than the time to live
     * period and remove them iteratively.
     */
    @SuppressWarnings("unchecked")
    public void cleanup() {

        long now = System.currentTimeMillis();
        ArrayList<K> deleteKey = null;

        synchronized (lruMap) {

            MapIterator iterator = lruMap.mapIterator();

            deleteKey = new ArrayList<K>((lruMap.size() / 2) + 1);

            K key = null;
            CacheObject object = null;

            while (iterator.hasNext()) {

                key = (K) iterator.next();
                object = (CacheObject) iterator.getValue();

                if (object != null && (now > (object.lastAccessed + timeToLive))) {
                    deleteKey.add(key);
                }
            }
        }

        for (K key : deleteKey) {

            synchronized (lruMap) {
                lruMap.remove(key);
            }

            Thread.yield();
        }
    }

    /**
     * convert the cache full of items to regular HashMap with the same
     * key and value pair
     *
     * @return
     */
    public Map<Product, Integer> convertToMap() {

        synchronized (lruMap) {

            Map<Product, Integer> map = new HashMap<>();
            MapIterator iterator = lruMap.mapIterator();

            K k = null;
            V v = null;

            CacheObject o = null;

            while (iterator.hasNext()) {

                k = (K) iterator.next();
                v = (V) iterator.getValue();

                Product product = (Product) k;

                // this fails right here
                int value = Integer.parseInt(String.valueOf(v));

                map.put(product, value);
            }

            return map;
        }
    }

}

在API类中,它是作为

引入的
MemoryCache<Product, Integer> cache = new MemoryCache<>(1800, 500, 10000); 

我将商品数据与API类中出售的商品一起存储

cache.put(product, 0);

下面定义的产品类别

@Entity
public class Product {

    @Id
    @Column(name = "productId")
    private String productId;

    @Column(name = "stockId")
    private String id;

    @Column(name = "stock_timestamp")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "UTC")
    private Timestamp timestamp;

    @Column(name = "quantity")
    private int quantity;


    public Product() {
    }

    public Product(String productId, Timestamp requestTimestamp, String id, Timestamp timestamp, int quantity) {
        this.productId = productId;
        this.id = id;
        this.timestamp = timestamp;
        this.quantity = quantity;
    }


   // getter, setter 

   // equals and hasCode 

   // toString
}

convertToMap类中的MemoryCache方法获取缓存存储并将其转换为HashMap。该方法在我尝试将int存储为值的那一行中有一个错误。

int value = Integer.parseInt(String.valueOf(v));

我有调试会话的屏幕截图。

enter image description here

如您所见,我需要获取值(即1000、100),并将其作为目标value的{​​{1}}。为此目的编写HashMap方法的正确方法是什么?

2 个答案:

答案 0 :(得分:2)

泛型的目的是避免强制转换,从而使代码更健壮,但是您使用泛型的方式会在某种程度上击败它。
您的问题完美地说明了这一点:

MapIterator iterator = lruMap.mapIterator(); // no type associated to the iterator
// ....
k = (K) iterator.next(); // unsafe conversion
v = (V) iterator.getValue();    // unsafe conversion
Product product = (Product) k; // unsafe conversion    
// this fails right here
int value = Integer.parseInt(String.valueOf(v)); // how to be sure that v is an Integer ?

LRUMapMapIterator可能是自定义类,必须是通用类,它们与MemoryCache<K,V>类似,因此使整个过程保持一致。
同样,泛型类中指定的此方法显然滥用了地图的类型。您从类中声明的泛型类型传递到ProductInteger

public class MemoryCache<K, V> {
    //..
    public Map<Product, Integer> convertToMap() {}
    //..
}

最后,它使您的MemoryCache设计为仅与ProductInteger具体类型的KV一起使用。在这种情况下,泛型是无用的,您必须将其删除。
如果以后您想要/需要一个更通用的解决方案,请在通用应用程序中继续进行,您应该以定义为convertToMap()的结尾:

public Map<K, V> convertToMap() {...}

答案 1 :(得分:1)

您在LruMap键中看到的键的类型为Product,但值的类型为MemoryCache$CacheObject,而不是Integer

因此您需要将代码更改为

int value = Integer.parseInt(String.valueOf(v.value)); //Assuming v is of type MemoryCache$CacheObject

或者您甚至可以使用此

Integer value = (Integer) v.value;