比较两个对象的不同属性集

Comparing two objects for varying set of properties

我有一个数据class。字段可以是集合、基元、引用等。我必须检查此 class 的两个实例是否相等。通常我们为此目的重写 equals 方法。但是用例是这样的,要比较的属性可能会有所不同。

所以说,class A 有属性:

int name:
int age:
List<String> hobbies;

在一次调用中,我可能必须根据姓名、年龄检查是否相等,而对于另一次调用,我可能必须检查姓名、爱好是否相等。

实现此目标的最佳做法是什么?

anyMatch

您可以使用 Stream API 解决此问题,使用 anyMatch,您的规则基本上被定义为 Predicates。

例如检查是否有 20 岁的人:

List<Person> persons = ...

boolean doesAnyMatch = persons.stream()
    .anyMatch(p -> p.getAge() == 20);

您当然也可以将规则设置为与现有项目进行比较的方式,稍微模仿 equals

p -> p.getAge() == otherPerson.getAge()

Predicate

您可以在其他地方设置所有规则,如 Predicates 然后使用它们。例如:

List<Predicate<Person>> rules = List.of(
    p -> p.getAge() == 20,
    p -> p.getName().equals("John"),
    p -> p.getAge() > 18,
    p -> p.getName().length() > 10 && p.getAge() < 50
);

然后可以在某种循环中使用它们,无论您需要什么:

for (Predicate rule : rules) {
    boolean doesAnyMatch = persons.stream()
        .anyMatch(rule);
    ...
}

findAny

您可以将 anyMatch 替换为 filterfindAny 的组合以获得实际匹配,即 Person,而不仅仅是 boolean:

Person matchingPerson = persons.stream()
    .filter(rule)
    .findAny();

假设您想始终通过 2 个属性进行比较,那么对于 Java8,最好先使用 class 函数。

private boolean equalNameAndAge(A a1, A a2) {
    return compareByTwoParameters(a1, a2, A::getName, A::getAge);
}

private boolean equalNameAndHobbies(A a1, A a2) {
    return compareByTwoParameters(a1, a2, A::getName, A::getHobbies);
}

private boolean compareByTwoParameters(A a1, A a2, Function<A, ?>... functions) {
    if (a1 == null || a2 == null) {
        return a1 == a2;
    }
    for (Function<A, ?> function : functions) {
        if (!Objects.equals(function.apply(a1), function.apply(a2))) {
            return false;
        }
    }
    return true;
}