缓存使用多个参数构建的对象 [英] Caching objects built with multiple parameters

查看:103
本文介绍了缓存使用多个参数构建的对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个工厂创建类 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;
    }

}

解决方案

您实际上不应该将哈希码用作地图中的键.一个类的哈希码并非一定要保证该类的任何两个不相等的实例都不会相同.确实,您的哈希码方法肯定可以为两个不相等的实例产生相同的哈希码.您需要MyClass上实现equals,以基于它们包含的Stringint的相等性来检查MyClass的两个实例是否相等.我还建议将si字段设置为final,以便如果您将以这种方式使用它,则可以更强地保证每个MyClass实例的不变性.

除此之外,我认为您实际上想要的是 interner ....也就是说,可以保证您最多只能存储给定MyClass的1个实例.一次存储在内存中.正确的解决方案是Map<MyClass, MyClass> ...更具体地讲,如果有可能从多个线程调用getOrCreateMyClass,则更确切地说是ConcurrentMap<MyClass, MyClass>.现在,您确实需要创建一个MyClass的新实例,以便在使用这种方法时检查缓存,但这确实是不可避免的...并且这没什么大不了的,因为MyClass易于创建.

番石榴具有可以为您完成所有工作的功能: 解决方案

You really shouldn't be using the hashcode as the key in your map. A class's hashcode is not intended to necessarily guarantee that it will not be the same for any two non-equal instances of that class. Indeed, your hashcode method could definitely produce the same hashcode for two non-equal instances. You do need to implement equals on MyClass to check that two instances of MyClass are equal based on the equality of the String and int they contain. I'd also recommend making the s and i fields final to provide a stronger guarantee of the immutability of each MyClass instance if you're going to be using it this way.

Beyond that, I think what you actually want here is an interner.... that is, something to guarantee that you'll only ever store at most 1 instance of a given MyClass in memory at a time. The correct solution to this is a Map<MyClass, MyClass>... more specifically a ConcurrentMap<MyClass, MyClass> if there's any chance of getOrCreateMyClass being called from multiple threads. Now, you do need to create a new instance of MyClass in order to check the cache when using this approach, but that's inevitable really... and it's not a big deal because MyClass is easy to create.

Guava has something that does all the work for you here: its Interner interface and corresponding Interners factory/utility class. Here's how you might use it to implement getOrCreateMyClass:

private static final Interner<MyClass> interner = Interners.newStrongInterner();

public static MyClass getOrCreateMyClass(String s, int i) {
  return interner.intern(new MyClass(s, i));
}

Note that using a strong interner will, like your example code, keep each MyClass it holds in memory as long as the interner is in memory, regardless of whether anything else in the program has a reference to a given instance. If you use newWeakInterner instead, when there isn't anything elsewhere in your program using a given MyClass instance, that instance will be eligible for garbage collection, helping you not waste memory with instances you don't need around.

If you choose to do this yourself, you'll want to use a ConcurrentMap cache and use putIfAbsent. You can take a look at the implementation of Guava's strong interner for reference I imagine... the weak reference approach is much more complicated though.

这篇关于缓存使用多个参数构建的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆