将缓存对象转换为 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)并将其作为预期 HashMap
的 value
。为此,编写 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 ?
LRUMap
和 MapIterator
可能是自定义的 classes 必须 是通用的 classes 依赖于类似MemoryCache<K,V>
使整个事情保持一致。
同样,泛型 class 中指定的这种方法显然是对地图类型的滥用。您从 class 中声明的泛型类型传递到 Product
和 Integer
:
public class MemoryCache<K, V> {
//..
public Map<Product, Integer> convertToMap() {}
//..
}
最后,它使您的 MemoryCache
设计为仅与 Product
和 Integer
一起工作,作为 K
、V
具体类型。在这种情况下,泛型是没有用的,你必须删除它们。
如果稍后您 want/need 获得更通用的解决方案,请在通用应用程序中更进一步,您应该以 convertToMap()
定义为 :
public Map<K, V> convertToMap() {...}
我有一个内存缓存 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)并将其作为预期 HashMap
的 value
。为此,编写 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 ?
LRUMap
和 MapIterator
可能是自定义的 classes 必须 是通用的 classes 依赖于类似MemoryCache<K,V>
使整个事情保持一致。
同样,泛型 class 中指定的这种方法显然是对地图类型的滥用。您从 class 中声明的泛型类型传递到 Product
和 Integer
:
public class MemoryCache<K, V> {
//..
public Map<Product, Integer> convertToMap() {}
//..
}
最后,它使您的 MemoryCache
设计为仅与 Product
和 Integer
一起工作,作为 K
、V
具体类型。在这种情况下,泛型是没有用的,你必须删除它们。
如果稍后您 want/need 获得更通用的解决方案,请在通用应用程序中更进一步,您应该以 convertToMap()
定义为 :
public Map<K, V> convertToMap() {...}