插入密钥时自定义等于/ hash(Guava Cache)

时间:2012-09-05 14:57:46

标签: java hash equals guava

简单地说,我必须覆盖缓存选择正确密钥的方式,因为在检索密钥时不应考虑某些字段(例如,时间戳,消息ID等)。 我无法修改密钥对象的实际哈希函数,因为它已经用于在我的代码中识别。
是否可以使用番石榴缓存?有一个解决方法?

这是我的配置:

CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).recordStats().
    expireAfterWrite(DEFAULT_AGE, TimeUnit.DAYS).build(
    new CacheLoader<Request, Response>() {
        @Override
        public Response load(Request request) { 
            return request.getResponse();
        }
    });

这是我的哈希函数(在我的代码中的其他地方使用):

public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + code;
    result = prime * result + messageID; // <= **this one shouldn't evaluated**
    result = prime * result + Arrays.hashCode(payload);
    result = prime * result + (int) (timestamp ^ timestamp >>> 32); // <= **this one shouldn't evaluated**
    result = prime * result + (type == null ? 0 : type.hashCode());
    result = prime * result + version;
    return result;
}

顺便说一句,这种缓存是使用自己的哈希函数实现(例如,通过内省)还是使用默认缓存?

** 编辑: **
正如回答中所指出的,实现此结果的最佳方法是包装类。
我的解决方案

/**
 * Nested class to store a request as a key in the cache. It is needed to
 * normalize the variable fields of the normal requests.
 */
private static final class CachedRequest extends Request {

    private static CachedRequest fromRequest(Request request) {
        // set only the fields that cannot change between two same requests
        // ...
    }

    @Override
    public int hashCode() {
        HashFunction hashFunction = Hashing.md5();
        HashCode hashCode;
        // ...
        return hashCode.asInt();
    }

    @Override
    public boolean equals(Object obj) {
            // coherent with hashCode()
            // ...
    }
}

4 个答案:

答案 0 :(得分:14)

您只需将Request个对象包装到CachedRequest个对象中,其中CachedRequest将根据所需字段实现hashCode()equals(),并提供访问权限包裹Request

答案 1 :(得分:1)

如果你不能修改哈希函数(我仍然不明白为什么),那么你需要使用“包装”键来缓存,例如:

public class RequestKey {
  private final Request _req;

  public int hashCode() {
    // use appropriate Request fields here
  }

  public boolean equals(Object o) {
    return ((this == o) || ((o != null) && (getClass() == o.getClass()) && _req.equals(((RequestKey)o)._req)));
  }
}

答案 2 :(得分:1)

我很确定,番石榴是不可能的。有一些使用自定义等价的合法案例,但他们说它们太少了,无法由CacheBuilderMapMaker处理。 1 甚至{{1} },但它仅在内部使用(另请参阅herehere)。

您需要在要用于查找的字段中创建自己的密钥,或者将com.google.common.base.Equivalence包装在另一个定义Requestequals的对象中。

1 使用与hashCode / equals不同的内容的唯一情况是hashCodeweakKeys不再存在) ,然后它是softKeys / ==组合。在任何情况下,您都不能自由选择等价。

答案 3 :(得分:0)

如果您使用的是 Lombok:

有一个 Lombok Annotation @EqualsAndHashCode,它覆盖 hashCodeequals 以基于对象的非静态、非瞬态属性。因此,对于以下内容,如果您比较使用相同 nameagesalary 创建的两个员工,则在比较时,它们的计算结果为 true

@EqualsAndHashCode
public class Employee {

    private String name;
    private int age;
    private int salary;
}

如果您不想包含属性,可以用 @EqualsAndHashCode.Exclude 标记它们。

@EqualsAndHashCode
public class Employee {

    private String name;
    @EqualsAndHashCode.Exclude
    private int age;
    @EqualsAndHashCode.Exclude
    private int salary;
}

您还可以专门包含以下字段:

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Employee {

    @EqualsAndHashCode.Include
    private String name;
    @EqualsAndHashCode.Include
    private int age;
    private int salary;
}

Lombok 注释文档:https://projectlombok.org/features/EqualsAndHashCode

示例来自:http://www.javabyexamples.com/delombok-equalsandhashcode