我有一个工厂创建MyClass类的对象,并在它们存在时返回已生成的对象。由于我有采用多个参数的创建方法(getOrCreateMyClass),这是使用Map存储和检索对象的最佳方法吗?
我当前的解决方法如下,但是听起来我不太清楚。
我使用MyClass类的hashCode方法(稍作修改)基于MyClass类的参数构建一个int,并将其用作Map的键。
import java.util.HashMap;
import java.util.Map;
public class MyClassFactory {
static Map<Integer, MyClass> cache = new HashMap<Integer, MyClass>();
private static class MyClass {
private String s;
private int i;
public MyClass(String s, int i) {
}
public static int getHashCode(String s, int i) {
final int prime = 31;
int result = 1;
result = prime * result + i;
result = prime * result + ((s == null) ? 0 : s.hashCode());
return result;
}
@Override
public int hashCode() {
return getHashCode(this.s, this.i);
}
}
public static MyClass getOrCreateMyClass(String s, int i) {
int hashCode = MyClass.getHashCode(s, i);
MyClass a = cache.get(hashCode);
if (a == null) {
a = new MyClass(s, i);
cache.put(hashCode , a);
}
return a;
}
}
最佳答案
您实际上不应该将哈希码用作地图中的键。类的哈希码不一定要保证该类的任何两个非相等实例的哈希码都不会相同。确实,您的哈希码方法绝对可以为两个不相等的实例产生相同的哈希码。您确实需要在equals
上实现MyClass
,以基于它们包含的MyClass
和String
的相等性来检查int
的两个实例是否相等。我还建议将s
和i
字段设置为final
,以便如果您要以这种方式使用它,则可以更强地保证每个MyClass
实例的不变性。
除此之外,我认为您真正想要的是一个交互器....也就是说,可以保证您一次最多只能在给定的MyClass
实例中存储1个实例。正确的解决方案是Map<MyClass, MyClass>
...更具体地讲,如果有可能从多个线程中调用ConcurrentMap<MyClass, MyClass>
,则更确切地说是getOrCreateMyClass
。现在,您确实需要创建一个新的MyClass
实例,以便在使用这种方法时检查缓存,但这确实是不可避免的...并且这没什么大不了的,因为MyClass
易于创建。
Guava具有可以为您完成所有工作的功能:其Interner接口和相应的Interners工厂/实用程序类。这是您使用它实现getOrCreateMyClass
的方式:
private static final Interner<MyClass> interner = Interners.newStrongInterner();
public static MyClass getOrCreateMyClass(String s, int i) {
return interner.intern(new MyClass(s, i));
}
请注意,与示例代码一样,使用强大的合作伙伴会将其保存的每个
MyClass
保留在内存中,只要该合作伙伴位于内存中,无论程序中是否有其他引用特定实例的引用。如果改用newWeakInterner
,则当程序中没有其他任何给定的MyClass
实例使用该实例时,该实例将可以进行垃圾回收,从而帮助您避免不必要的实例浪费内存。如果选择自己执行此操作,则需要使用
ConcurrentMap
缓存并使用putIfAbsent
。我想您可以看看Guava强大的内部参考者的实现方式,我想...弱参考方法要复杂得多。