putIfAbsent具有综合参数相等推理



我有一个包私有接口,它能够根据捕获的合成参数引用识别相等性。

interface SynthEqual {
default Object synthParamAt(int paramIndex) {
try {
return getClass().getDeclaredFields()[paramIndex].get(this);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
default<S> boolean equalTo(int paramIndex, S other) {
return synthParamAt(paramIndex).equals(other);
}
//Edit:
// I realized this is a better option, instead of exposing the synthetic param:
default boolean equalTo(int at, SynthEqual that) {
return that != null && that.synthParamAt(at).equal(synthParamAt(at));
}
// and making synthParamAt private with Java9
}

此接口用于lambda捕获超过2个参数时,但我只对其中一个参数的相等感兴趣…

如果你散列这些合成参数,它们是不同的…即使它们来自相同的参考源。

但是如果你==或者。equal(),结果为真。

这就是为什么我宁愿不@Override等于或hashcode,相反,我总是使用我的普通自定义均衡器方法…,但是如果你希望/需要……你可以…理论上这样做:

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AClass<?> that = (AClass<?>) o;
return that.lambdaFieldA.equalTo(0, this.lambdaFieldA.synthParamAt(0)) && Objects.equals(fieldB, that.fieldB);
}

正如你所看到的,这个组合类正确地覆盖了equals,但是我的lambda使用了它自定义的合成相等测试。

作为hashCode…好吧,我不知道这怎么能被推翻……我认为不可能以一种可以推断合成参数的方式重写哈希。到目前为止,这还不是一个问题……

真正的问题出现在处理map时。

当我们查看ConcurrenthashMap的putVal()方法时,我们可以看到它们正在使用哈希函数在Node表中搜索匹配项。

final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
//Here
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
// Here the fetch
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// .......
// A few lines bellow:
else if (onlyIfAbsent // check first node without acquiring lock
//Here compares
&& fh == hash
&& ((fk = f.key) == key || (fk != null && key.equals(fk))) // FINALLY!!!
&& (fv = f.val) != null)
return fv;
.....
static final int spread(int h) {
return (h ^ (h >>> 16)) & HASH_BITS;
}

我可以看到,.equals只在哈希搜索找到匹配后才执行…所以我相信我需要修复我的哈希覆盖为我的等号能够测试。

我如何重写K键,使其在ConcurrentHashMap规则下正确处理合成参数等式?

好吧,这是一个部分的答案,我意识到我的问题有点懒惰,所以我决定测试更深入的东西。唯一缺少的是这个:

default int hashAt(int at) {
return paramAt(at).hashCode();
}

使结果接口:

interface SynthEqual {
private Object paramAt(int at) {
try {
return getClass().getDeclaredFields()[at].get(this);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
default <S> boolean equalTo(int at, S arg) {
return paramAt(at).equals(arg);
}
default<S> boolean equalTo(int at, SynthEqual that) {
return that != null && paramAt(at).equals(that.paramAt(at));
}
default int hashAt(int at) {
return paramAt(at).hashCode();
}
}

对于K键的唯一强制要求是它本身不是lambda实例。

key对象可以通过重写equals()和hasCode(),利用lambda默认测试并推断它们内部的合成参数相等。

static class LambdaWrapper {
final HeyThere lambda;
LambdaWrapper(HeyThere lambda) {
this.lambda = lambda;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LambdaWrapper that = (LambdaWrapper) o;
return lambda.equalTo(0, that.lambda);
}
@Override
public int hashCode() {
return lambda.hashAt(0);
}
}

最新更新