将缓存对象转换为 HashMap

Convert a cache object to HashMap

我有一个内存缓存 class,用于存储 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 class 中,它被介绍为

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

我将产品数据与 API class、

中出售的商品一起存储
cache.put(product, 0);

下面定义的产品class,

@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 class 中的 convertToMap 方法获取缓存存储并将其转换为 HashMap。该方法在我尝试将 int 存储为值的行中有一个错误。

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

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

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

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

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

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 可能是自定义的 classes 必须 是通用的 classes 依赖于类似MemoryCache<K,V> 使整个事情保持一致。
同样,泛型 class 中指定的这种方法显然是对地图类型的滥用。您从 class 中声明的泛型类型传递到 ProductInteger :

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

最后,它使您的 MemoryCache 设计为仅与 ProductInteger 一起工作,作为 KV 具体类型。在这种情况下,泛型是没有用的,你必须删除它们。
如果稍后您 want/need 获得更通用的解决方案,请在通用应用程序中更进一步,您应该以 convertToMap() 定义为 :

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