I have a data class. Fields can be collections, primitives, references etc. I have to check the equality of two instances of this class. Generally we override equals method for this purpose. But usecase is such that properties which are to be compared may vary.
So say, class A has properties:
int name:
int age:
List<String> hobbies;
In one invocation I may have to check equality based on name,age, and for another invocation I may have to check equality for name, hobbies.
What is best practice to achieve this?
Let's say you want to compare always by 2 properties, then with Java 8 it's best to use first class functions.
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;
}
anyMatch
You can solve this with the Stream API, using anyMatch
, with your rules basically being defined as Predicate
s.
For example checking if there is any person who is 20
years old:
List<Person> persons = ...
boolean doesAnyMatch = persons.stream()
.anyMatch(p -> p.getAge() == 20);
You can of course also setup the rule in a way that it compares with an existing item, mimicking equals
a bit more:
p -> p.getAge() == otherPerson.getAge()
Predicate
You can setup all your rules somewhere else, as Predicate
s and then use them. For example:
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
);
And then maybe use them in some sort of loop, whatever you need:
for (Predicate rule : rules) {
boolean doesAnyMatch = persons.stream()
.anyMatch(rule);
...
}
findAny
You can substitute anyMatch
by a combination of filter
and findAny
to receive an actual match, i.e. a Person
, instead of just a boolean
:
Person matchingPerson = persons.stream()
.filter(rule)
.findAny();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With