How to Compare Two Java Objects

Compare two objects with .equals() and == operator

== compares object references, it checks to see if the two operands point to the same object (not equivalent objects, the same object).

If you want to compare strings (to see if they contain the same characters), you need to compare the strings using equals.

In your case, if two instances of MyClass really are considered equal if the strings match, then:

public boolean equals(Object object2) {
return object2 instanceof MyClass && a.equals(((MyClass)object2).a);
}

...but usually if you are defining a class, there's more to equivalency than the equivalency of a single field (a in this case).


Side note: If you override equals, you almost always need to override hashCode. As it says in the equals JavaDoc:

Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

How to use compareTo() to compare two objects?

Your class needs to implement Comparable interface, and inside your class you need to override compareTo method (which is in Comparable interface). In this method you will define how your object will be compared to another, on what 'criteria' the comparison will be done.

It's a good practice to implement compareTo method such that, when its return value is 0, it's like two objects are equals (e.g. firstK.compareTo(secondK) == firstK.equals(secondK)).

You can also read Java documentation compareTo documentation.

public class K implements Comparable<K> {
//Attributes, constructor etc
@Override
public int compareTo(final K otherK) {
/*
Do what you want to compare them, for example, if your
K class has a integer field 'value', and you want to compare
K's instances based on 'value' field, you can do as follow:
*/
return Integer.compare(this.value, otherK.getValue());
}
}

Comparing the content of two objects in Java

You can structure your equals method similarly to an if statement. You just need to cast the other argument first before your can compare their fields.

@Override
public boolean equals(Object o) {
// Check if the other object is also a Student
if (o instanceof Student) {
// Now that we know o is a student, we can safely cast it
Student other = (Student) o;

// Similarly to how you would write an if statement you can compare each individual field.
// Thanks to inheritance, we defer the equality check of each field to its own implementation
return this.firstName.equals(other.firstName)
&& this.lastName.equals(other.lastName)
&& this.birthDate.equals(other.birthDate);
}

// Other object was not a student
return false;
}

You then need to go write something similar in Date so that when you compare birthDate, it will know what to do.

You can also take this one step farther by using Objects.equals(a, b) instead of a.equals(b). This will ensure you do not get a nullPointerException if a happens to be null when comparing them. However, since this looks to be a school project I imagine you may be expected to either check manually or assume the values will not be null instead of using the standard library.

Compare two objects excluding some fields - Java

The quickest way without writing any code is Lombok

Lombok is one of the most used libraries in java and it takes a lot of Boilerplate code off your projects. If you need to read more on what it can and does, go here.

The way to implement what you need is pretty straightforward:

// Generate the equals and HashCode functions and Include only the fields that I annotate with Include
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Getter // Generate getters for each field
@Setter // Generate setters for each field
public class Class1
{

@EqualsAndHashCode.Include // Include this field
private Long identity;

private String testStr1; // This field is not annotated with Include so it will not be included in the functions.

// ... any other fields
}

Lombok can do a lot more than this. For more information on @EqualsAndHashCode refer to this.

You can always use @EqualsAndHashCode.Exclude for a quicker solution to your use case:

@EqualsAndHashCode
@Getter // Generate getters for each field
@Setter // Generate setters for each field
public final class Class1 {
private String a;
private String b;
private String c;
:
:
:

private String z;

@EqualsAndHashCode.Exclude
private Date createdAt;
@EqualsAndHashCode.Exclude
private Date updatedAt;
}

How to compare two Sets of objects Not overriding equals/hashCode with Comparator and Streams

A Fix for Comparator

In order to create a Comparator in this case you need to provide generic type information explicitly, like that: <Book, String>comparing(), where Book and String are the parameter type and return type of the Function that Comparator.comparing() expects as a parameter.

Without an explicit declaration, the compiler has not enough data to determine the type of the variable book, and inside both comparing() and thenComparing() its type will be inferred as Object.

Comparator<Book> comparingBooks = 
Comparator.<Book, String>comparing(book -> book.getName())
.thenComparing(book -> book.getType());

If the case if only one of the static methods was used, the type of the book variable will be correctly inferred by the compiler as Book based on the type of the locale variable Comparator<Book> comparingBooks.

Both method lambda expressions could be replaced with method references:

Comparator<Book> comparingBooks =
Comparator.<Book, String>comparing(Book::getName)
.thenComparing(Book::getType);

for information on the syntax of generic methods, take a look at this tutorial

for more information on how to build comparators with Java 8 methods take a look at this tutorial

Stream

should validate any difference of Name and Type by returning true, if both Sets have exactly the same objects, it should return false.

Stream s1.stream().anyMatch(a -> s2.stream().anyMatch(b -> ...)) is currently checking whether there's an element in the first set s1 that is different from ANY of the elements in s2. I.e. the nested anyMatch() will return true for the first element encountered in s1 that has not matching element in s2. After the comparison of Book("book 1", BookType.ONE) (s1) with Book("book 2", BookType.TWO) (s2) anyMatch() terminates by returning true. And enclosing anyMatch() operation propagates this result.

Precisely the same will happen with the second example. The same pair of elements differ, although you've changed a type, names are not equal. Result is true.

Note:

  • that by convention classes are usually named with singular nouns, i.e. Book (not Books), Event, Person, etc.
  • as a rule of thumb, if your objects are intended to be used with collection they must implement equals/hashCode contract, your requirement is very unusual.

Comparing two objects for varying set of properties

anyMatch

You can solve this with the Stream API, using anyMatch, with your rules basically being defined as Predicates.

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

How to compare two java objects

You need to provide your own implementation of equals() in MyClass.

@Override
public boolean equals(Object other) {
if (!(other instanceof MyClass)) {
return false;
}

MyClass that = (MyClass) other;

// Custom equality check here.
return this.field1.equals(that.field1)
&& this.field2.equals(that.field2);
}

You should also override hashCode() if there's any chance of your objects being used in a hash table. A reasonable implementation would be to combine the hash codes of the object's fields with something like:

@Override
public int hashCode() {
int hashCode = 1;

hashCode = hashCode * 37 + this.field1.hashCode();
hashCode = hashCode * 37 + this.field2.hashCode();

return hashCode;
}

See this question for more details on implementing a hash function.

How to compare two objects without knowing their type

The problem you're reporting is that

return (String)ob;

should be

return ob.toString();

if ob is never null; or

return Objects.toString(ob);

if it might be.


But note that wrapping an object in an object to compare its toString() isn't necessary. You can simply use a Comparator<Object>, like Guava's Ordering.usingToString():

int order = Ordering.usingToString().compare(someObject, someOtherObject);

or whatever the Java 8 equivalent is. I guess it would be something like:

Comparator<Object> usingToString = (a, b) -> a.toString().compareTo(b.toString());
int order = usingToString(someObject, someOtherObject);


Related Topics



Leave a reply



Submit