我正在尝试创建一个通用的解析字符串缓存,以防止反复重建同一个对象。基本上,我正在构建的内容如下:
public class ParsedStringCache {
// static Map<Pair<String, Parser<T>>, T> _cache
// = new HashMap<Pair<String, Parser<T>>, T>();
public interface StringParser<T> {
public T parseString(String stringToParse);
}
public static <T> T getParsedObject(String stringToParse, Parser<T> parser) {
return parser.parseString(stringToParse);
}
}
这很好用,直到我尝试实际缓存结果,例如在上面的代码中使用类似于注释掉的hashmap的东西,这实际上会记住getParsedObject的结果。是否有一种相当简单的方法来避免投射?
答案 0 :(得分:3)
我会将缓存作为解析器的一部分。我确定你会遇到一些你需要解决的编译错误,但这是我要做的基础知识。
import java.util.*;
abstract class Parser<T> {
private Map<String,T> cache = new HashMap<String,T>();
public final T parseString(String str) {
T result = cache.get(str);
if(result == null) {
result = parseString0(str);
cache.put(str,result);
}
return result;
}
protected abstract T parseString0(String str);
}
public class IntParser extends Parser<Integer> {
protected Integer parseString0(String str) {
return Integer.parseInt(str.trim());
}
}
public class LongParser extends Parser<Long> {
protected Long parseString0(String str) {
return Long.parseLong(str.trim());
}
}
class ParserTest {
public static void main(String[] args) {
Parser<Integer> intParse = new IntParser();
Parser<Long> longParse = new LongParser();
Long long1 = longParse.parseString("10000");
Long long2 = longParse.parseString("20000");
Long long3 = longParse.parseString("30000");
Long equalLong = longParse.parseString("20000"); // repeat long2
Long fakeLong = new LongParser().parseString("20000"); // repeated with fake
System.out.println("Expecting true: " + (long2 == equalLong));
System.out.println("Expecting false: " + (fakeLong == equalLong));
}
}
C:\Documents and Settings\glowcoder\My Documents>javac Parser.java
C:\Documents and Settings\glowcoder\My Documents>javac IntParser.java
C:\Documents and Settings\glowcoder\My Documents>javac LongParser.java
C:\Documents and Settings\glowcoder\My Documents>javac ParserTest.java
C:\Documents and Settings\glowcoder\My Documents>java ParserTest
Expecting true: true
Expecting false: false
我会保持他们的管理方式,但添加方法来收集信息并向中央经理报告。显然,您需要向解析器添加更多方法以跟踪缓存。或者(这可能是一个更好的主意)您可以将缓存逻辑合并到Cache类中,让管理器跟踪这些缓存。
通过这种方式,您可以让缓存本身确定如何修剪其信息。它可以是最古老的,也可以是大小,它可以是各种标准。也许它有用户信息,您希望付费用户更多地留在缓存中。
import java.util.*;
class ParserCacheManager {
private Set<Parser<?>> parsers = new HashSet<Parser<?>>();
public void addParser(Parser<?> p) { parsers.add(p); }
public int size() {
int size = 0;
for(Parser<?> p : parsers) size += p.cacheSize();
return size;
}
public void trimToSize(int maxSize) {
while(size() > maxSize) {
Parser<?> p = largestParser();
p.trimToPercent(0.90);
}
}
/**
* This would be better perhaps with a set sorted by
* size, or something like that. But this works for example.
*/
private Parser<?> largestParser() {
Parser<?> largest = null;
for(Parser<?> p : parsers) {
if(largest == null || p.size() > largest.size())
largest = p;
}
return largest;
}
}
答案 1 :(得分:1)
除非您将此作为练习,否则请考虑使用Guava的CacheBuilder(或更通用的MapMaker)来执行此任务。它将处理所有并发和过期,并且可以轻松,流畅地配置:
Cache<String, T> parsedObjectCache = CacheBuilder.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(
new CacheLoader<String, T>() {
public T load(String str) throws MyParseException {
return parse(str); //called either on or from inside a Parser<T>
}
});
如果你想要一个集中式缓存,你需要编写一个自定义密钥类,它包含对String
和Parser<?>
的引用,并覆盖hashcode / equals。
但是,您可能最好为每个解析器实现单独的Cache<String, T>
,因为尝试将所有内容存储在同一Collection
中时,类型信息将会丢失。
妥协方法是保留一个主Map<Parser<?>, Cache<String, ?>>
,您可以使用Cache
根据Parser
查找每个Class<?>
(或者使用T
Cache<String, T>
1}}作为关键)。但是,你仍然会以这种方式丢失通用类型信息,并且必须进行投射。
您可以考虑@glowcoder's suggestion并将Parser<T>
整合到每个{{1}}中。这似乎是一种在不进行转换的情况下维护泛型类型信息的合理方法。
答案 2 :(得分:0)
你必须使ParserStringCache成为通用的,你不能在静态类变量上使用任意泛型参数,它必须是非静态成员。
public class ParsedStringCache< T > {
Map<Pair<String, Parser<T>>, T> _cache =
new HashMap<Pair<String, Parser<T>>, T>();
}