putifabsent具有综合论证平等推理

发布于 2025-02-08 13:13:19 字数 2635 浏览 1 评论 0原文

我有一个软件包的私人接口,能够根据捕获的合成参数参考来识别平等。

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个以上的Arguemnts时,该界面用于使用该界面,但是我只对其中一个的平等感兴趣...

如果您。如果您使用此综合参数,它们也不同...即使它们来自同一参考源。

但是,如果您==或.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);
    }

正如您可以看到的那样 :课程适当地覆盖了平等,但是我的lambda正在使用其自定义合成平等测试。

至于哈希码...好吧,我不知道这将如何被覆盖... 我认为,不可能以一种可以推断出综合论点的方式覆盖哈希。 到目前为止,这从来都不是一个问题……

在处理地图时,真正的问题出现了。

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

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;
    }

我可以看到,只有在哈希搜索找到匹配项之后才能执行。

我如何以这样的方式覆盖k密钥,以使其适当地处理合成论点的平等性?

I have a package private interface that's able to identify equality based on captured synthetic argument references.

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
}

This interface is used for when lambda captures more than 2 arguemnts, but I am interested only on the equality of one of them...

If you .hash this synthetic arguments they are different... EVEN IF they come from the same reference source.

But if you == Or .equal() the result comes as true.

This is why I rather NOT @Override equals or hashcode, instead I always use my plain custom equalizer methods..., BUT if you wish/need... you could... in theory do this:

    @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);
    }

As you can see this composed class is properly overriding equals, but my lambda is using it's custom synthetic equality test.

as for hashCode... well I don't know how would that be overridden...
I believe it is not possible to override hash in such a way that synthetic arguments can be inferred.
Up to now, this has never been an issue...

The real problem comes when dealing with Maps.

When we look at the putVal() method of ConcurrenthashMap we can see that they are using the hash function to search for a match in the Node table.

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;
    }

I can see, that .equals is performed ONLY After a hash search has found a match... So I believe I need to fix my hash override for my equals to be able to test.

How could I override the K key in such a way that it properly deals with synthetic argument equalities under the rules of a ConcurrentHashMap?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

枉心 2025-02-15 13:13:19

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

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实例。

然后,关键对象可以通过覆盖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);
    }
}

Ok this is a partial answer, I realized my question was kind of lazy, so I decided to test things with more depth. The only thing missing was this:

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

So that the resulting interface:

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();
    }
}

The only obligatory requirement for a K key then is to NOT be a lambda instance in itself.

The key object could then, by overriding equals() and hasCode(), make use of our lambda default tests and infer their inner synthetic arguments equality.

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);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文