【问题标题】:Convert a cache object to HashMap将缓存对象转换为 HashMap
【发布时间】:2019-07-27 10:08:50
【问题描述】:

我有一个内存缓存类,用于存储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
}

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

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

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

如您所见,我需要获取值(即 1000、100)并将其作为预期 HashMapvalue。为此目的编写convertToMap 方法的正确方法是什么?

【问题讨论】:

    标签: java caching hashmap lru


    【解决方案1】:

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

    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&lt;K,V&gt; 的通用类以使整个事情保持一致。
    同样,在泛型类中指定的这种方法显然是对映射类型的滥用。您从类中声明的泛型类型传递给 ProductInteger

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

    最后,它使您的 MemoryCache 设计为仅适用于 ProductInteger 作为 KV 混凝土类型。在这种情况下,泛型是无用的,您必须删除它们。
    如果稍后您想要/需要获得更通用的解决方案,请在通用应用程序中更进一步,您应该以定义为的 convertToMap() 结尾:

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

    【讨论】:

    • 感谢您的意见,我找到了问题的解决方案。
    • @Arefe 欢迎您。这是非常重要的一点。如果您找到的解决方案依赖于随机强制转换,那么您的缓存将真的不健壮和一致。我更新以解释更多。定义一个带有一些设计/运行时问题的缓存类确实是要避免的。
    【解决方案2】:

    正如您在 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; 
    

    【讨论】:

    • 感谢您的帮助。你知道,我自己在论坛上发帖后就发现了这个错误,而我花了几个小时之前调查了它。我的收获是,当您将问题写在清晰的宣言中时,您就解决了大部分问题。
    猜你喜欢
    • 2010-10-21
    • 2016-12-22
    • 2022-11-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-30
    • 1970-01-01
    • 1970-01-01
    • 2022-11-24
    相关资源
    最近更新 更多