在 class 之外覆盖 Object#equals(Object)

Overriding Object#equals(Object) outside the class

使用 list.contains(someObject) 时是否可以在本地覆盖 Object#equals(Object)

示例:

class SomeObject {
    ...
    private int id;
    private String name;

    @Overrdide
    public boolean equals(Object other){
         ...
         return this.id == other.id;
    }
}

但是如果我在使用 list.contains(someObject) 时想要另一种 equals 怎么办?例如我想知道一个列表是否包含某个名字?是否可以覆盖 Object#equals(Object) 'anonymously'?

更具体的解释为什么我需要它:

int objectId = ... // Some event which passes me the attribute of an object but not the object itself

现在我有 List<SomeObject> someObjects,我想知道这个列表是否包含带有 objectId 的对象,而不必对其进行迭代。

我能想到的一个 "solution" 会使用 Map<Integer, SomeObject> mapping 然后 someObject = mapping.get(objectId)

编辑: 我的问题不是重复的,因为我特别要求覆盖 Object#equals(Object).

您可以对从列表中获得的流对象应用过滤器。过滤器采用谓词,您可以在其中设置条件。然后你可以检查过滤后的流是否不为空:

list.stream().filter(e -> e.name.equals(otherName)).findAny().isPresent()

您可以使其可重复使用,例如:

private <T> boolean containsWithPredicate(List<T> list, Predicate<T> predicate) {
    return list.stream().filter(predicate).findAny().isPresent();
}

并按如下方式调用它:

boolean containsElement = containsWithPredicate(myListOfObjects,
                                                 (e -> e.name.equals(otherName)));

编辑:

有一种更简单的方法可以通过调用 Stream.anyMatch 而不是先调用 filter 然后调用 findAny:

list.stream().anyMatch(predicate);

您可以使用带有自定义 ComparatorTreeSet 来实现您想要的效果。它不使用 equals() 表示相等,而是认为如果 compare() returns 0,对象是相等的。

假设我们希望所有 Strings 都相等,前提是它们的长度相同:

TreeSet<String> set = new TreeSet(new Comparator<>() {
    public int compare(String o1, String o2) {
       return o1.length() - o2.length();
    }
}
set.add("asd");
set.contains("foo");    // Returns true

如果您需要在不同的时间对对象使用不同的 "equality definitions",同时仍然使用集合,这可能是一个有用的构造(也适用于 TreeMap)。

同一个 class 不可能有多个 equals。但是您可以 subclass 您的 class 并覆盖 hashCode()equals(),然后根据 [=10] 使用所需的实例=]你想用。

我必须说我不喜欢这样做,equals 方法应该定义得很好,在不同的场景下使用多个 equals 会很混乱。如果我需要两种不同的 equals 方法,我将采用完全不同的实现而不是 subclassing。所以我强烈建议你重新考虑你的设计。