Comparing two list objects in Java is a common task in software development, and compare.edu.vn provides comprehensive guides on effectively accomplishing this. This article delves into various methods for comparing lists, ensuring you choose the optimal approach for your specific needs, including list comparison techniques, element-wise comparison, and performance considerations using Java List comparison.
1. Understanding the Basics of List Comparison in Java
In Java, a List
is an ordered collection of elements. Comparing two List
objects involves determining if they are equal or identifying the differences between them. Several factors influence the method you choose, including the type of elements in the lists, the order of elements, and the specific requirements of your comparison. This section explores foundational concepts and methods for list comparison.
1.1. What is a List in Java?
A List
in Java is an interface that extends the Collection
interface. It represents an ordered sequence of elements, allowing duplicate values and providing methods for accessing elements by their index. Common implementations of the List
interface include ArrayList
, LinkedList
, and Vector
.
- ArrayList: A resizable array implementation of the
List
interface. It offers fast access to elements via index but can be slower for insertions and deletions in the middle of the list. - LinkedList: A doubly-linked list implementation of the
List
interface. It provides efficient insertion and deletion operations but slower access to elements compared toArrayList
. - Vector: Similar to
ArrayList
, but it is synchronized, making it thread-safe. However, this synchronization comes at the cost of performance.
1.2. Why Compare Lists?
Comparing lists is essential for various reasons, such as:
- Data Validation: Ensuring that two lists contain the same data, which is crucial in data processing and validation scenarios.
- Change Detection: Identifying differences between two versions of a list, useful in tracking changes in data sets.
- Testing: Verifying that the actual output of a method matches the expected output when the output is a list.
- Synchronization: Ensuring that two lists are synchronized or contain the same elements in distributed systems.
1.3. Basic Methods for List Comparison
Java provides several basic methods for comparing lists:
equals()
Method: Theequals()
method is the most straightforward way to compare two lists. It returnstrue
if both lists have the same size and all corresponding elements are equal.- Iterative Comparison: This involves iterating through both lists and comparing elements at each index. This method provides more control and flexibility, allowing you to implement custom comparison logic.
- Using
hashCode()
: While not a direct comparison method, thehashCode()
method can be used to quickly check if two lists are potentially equal before performing a more detailed comparison.
2. Using the equals()
Method for List Comparison
The equals()
method is the simplest and most common way to compare two lists in Java. It checks if both lists have the same size and if all corresponding elements are equal using the equals()
method of the elements themselves.
2.1. How the equals()
Method Works
The equals()
method in the List
interface is implemented by its concrete classes, such as ArrayList
and LinkedList
. The implementation typically follows these steps:
- Check for Identity: If the two list objects are the same instance, the method returns
true
. - Check for Null: If one list is
null
and the other is not, the method returnsfalse
. - Check for Class Type: Ensure that the object being compared is a
List
. - Check for Size: If the lists have different sizes, the method returns
false
. - Iterate and Compare: Iterate through both lists, comparing elements at each index using the
equals()
method of the elements. If any pair of elements is not equal, the method returnsfalse
. - Return True: If all elements are equal, the method returns
true
.
2.2. Example: Comparing Two ArrayList
Objects Using equals()
Consider the following example where we compare two ArrayList
objects containing strings:
import java.util.ArrayList;
import java.util.List;
public class ListComparisonExample {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("apple");
list1.add("banana");
list1.add("orange");
List<String> list2 = new ArrayList<>();
list2.add("apple");
list2.add("banana");
list2.add("orange");
List<String> list3 = new ArrayList<>();
list3.add("apple");
list3.add("orange");
list3.add("banana");
System.out.println("list1 equals list2: " + list1.equals(list2)); // Output: true
System.out.println("list1 equals list3: " + list1.equals(list3)); // Output: false
}
}
In this example, list1
and list2
are equal because they contain the same elements in the same order. list1
and list3
are not equal because the order of elements is different.
2.3. Considerations When Using equals()
- Element Equality: The
equals()
method relies on theequals()
method of the elements in the list. If the elements are custom objects, ensure that theirequals()
method is properly implemented to compare their content. - Order Matters: The
equals()
method considers the order of elements. If the order is not important, you may need to sort the lists before comparing them. - Null Elements: The
equals()
method handlesnull
elements correctly. If both lists containnull
at the same index, they are considered equal at that position.
3. Iterative Comparison of Lists
Iterative comparison involves manually iterating through the lists and comparing elements at each index. This method provides more flexibility and control, allowing you to implement custom comparison logic.
3.1. Implementing Iterative Comparison
To implement iterative comparison, you need to:
- Check the Size: Ensure that both lists have the same size. If not, they cannot be equal.
- Iterate Through the Lists: Use a loop to iterate through the lists, comparing elements at each index.
- Compare Elements: Use the
equals()
method or a custom comparison logic to compare the elements. - Handle Differences: If any pair of elements is not equal, the lists are not equal.
3.2. Example: Iterative Comparison of Two Lists
Here’s an example demonstrating iterative comparison:
import java.util.ArrayList;
import java.util.List;
public class IterativeListComparison {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
List<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
List<Integer> list3 = new ArrayList<>();
list3.add(1);
list3.add(3);
list3.add(2);
System.out.println("list1 equals list2: " + compareListsIteratively(list1, list2)); // Output: true
System.out.println("list1 equals list3: " + compareListsIteratively(list1, list3)); // Output: false
}
public static boolean compareListsIteratively(List<?> list1, List<?> list2) {
if (list1.size() != list2.size()) {
return false;
}
for (int i = 0; i < list1.size(); i++) {
if (!list1.get(i).equals(list2.get(i))) {
return false;
}
}
return true;
}
}
In this example, the compareListsIteratively
method compares two lists element by element. If any elements are not equal, it returns false
.
3.3. Advantages of Iterative Comparison
- Custom Logic: Allows you to implement custom comparison logic, such as ignoring case for strings or comparing specific fields of custom objects.
- Early Exit: You can exit the comparison early if a difference is found, which can improve performance in some cases.
- Flexibility: Provides more control over the comparison process, allowing you to handle
null
values or other special cases differently.
3.4. Disadvantages of Iterative Comparison
- More Code: Requires more code compared to using the
equals()
method. - Potential for Errors: More manual code increases the risk of introducing errors, such as off-by-one errors in the loop.
4. Using hashCode()
for Quick List Comparison
The hashCode()
method can be used to quickly check if two lists are potentially equal before performing a more detailed comparison. If two lists have different hash codes, they cannot be equal. However, if they have the same hash code, they may or may not be equal, so a more thorough comparison is still necessary.
4.1. How hashCode()
Works
The hashCode()
method in the List
interface returns an integer value that represents the hash code of the list. The hash code is calculated based on the elements in the list and their order. If two lists have the same elements in the same order, they will have the same hash code.
4.2. Example: Using hashCode()
to Compare Lists
Here’s an example demonstrating how to use hashCode()
for list comparison:
import java.util.ArrayList;
import java.util.List;
public class HashCodeListComparison {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("apple");
list1.add("banana");
list1.add("orange");
List<String> list2 = new ArrayList<>();
list2.add("apple");
list2.add("banana");
list2.add("orange");
List<String> list3 = new ArrayList<>();
list3.add("apple");
list3.add("orange");
list3.add("banana");
System.out.println("list1 hashCode: " + list1.hashCode());
System.out.println("list2 hashCode: " + list2.hashCode());
System.out.println("list3 hashCode: " + list3.hashCode());
System.out.println("list1 and list2 are potentially equal: " + (list1.hashCode() == list2.hashCode())); // Output: true
System.out.println("list1 and list3 are potentially equal: " + (list1.hashCode() == list3.hashCode())); // Output: false
// Further comparison using equals() is needed to confirm equality
System.out.println("list1 equals list2: " + list1.equals(list2)); // Output: true
System.out.println("list1 equals list3: " + list1.equals(list3)); // Output: false
}
}
In this example, list1
and list2
have the same hash code, indicating that they are potentially equal. A further comparison using the equals()
method confirms that they are indeed equal. list1
and list3
have different hash codes, indicating that they are not equal.
4.3. When to Use hashCode()
- Performance Optimization: Use
hashCode()
as a quick check before performing a more expensive comparison usingequals()
. - Large Lists: Useful for large lists where comparing all elements can be time-consuming.
- Hash-Based Data Structures: When using lists as keys in hash-based data structures like
HashMap
orHashSet
, thehashCode()
method is used to determine the bucket for the list.
4.4. Limitations of hashCode()
- Hash Collisions: Different lists can have the same hash code (hash collision), so you still need to use
equals()
to confirm equality. - Not a Definitive Comparison:
hashCode()
only provides a potential indication of equality, not a definitive answer.
5. Comparing Lists with Custom Objects
When lists contain custom objects, the comparison becomes more complex. You need to ensure that the equals()
method of the custom objects is properly implemented to compare their content.
5.1. Implementing equals()
in Custom Objects
To compare custom objects in a list, you must override the equals()
method in the class. The equals()
method should compare the relevant fields of the objects to determine if they are equal.
Here’s an example of a custom Employee
class with an overridden equals()
method:
public class Employee {
private int id;
private String name;
private int age;
public Employee(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Employee employee = (Employee) obj;
return id == employee.id &&
age == employee.age &&
name.equals(employee.name);
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + name.hashCode();
result = 31 * result + age;
return result;
}
}
In this example, the equals()
method compares the id
, name
, and age
fields of the Employee
objects.
5.2. Example: Comparing Lists of Custom Objects
Here’s an example demonstrating how to compare lists of Employee
objects:
import java.util.ArrayList;
import java.util.List;
public class CustomObjectListComparison {
public static void main(String[] args) {
List<Employee> list1 = new ArrayList<>();
list1.add(new Employee(1, "John Doe", 30));
list1.add(new Employee(2, "Jane Smith", 25));
List<Employee> list2 = new ArrayList<>();
list2.add(new Employee(1, "John Doe", 30));
list2.add(new Employee(2, "Jane Smith", 25));
List<Employee> list3 = new ArrayList<>();
list3.add(new Employee(2, "Jane Smith", 25));
list3.add(new Employee(1, "John Doe", 30));
System.out.println("list1 equals list2: " + list1.equals(list2)); // Output: true
System.out.println("list1 equals list3: " + list1.equals(list3)); // Output: false
}
}
In this example, list1
and list2
are equal because they contain the same Employee
objects in the same order. list1
and list3
are not equal because the order of the Employee
objects is different.
5.3. Importance of Implementing hashCode()
When you override the equals()
method, it’s also important to override the hashCode()
method. The hashCode()
method should generate a hash code based on the same fields used in the equals()
method. This ensures that if two objects are equal according to equals()
, they have the same hash code.
The hashCode()
method in the Employee
class is implemented as follows:
@Override
public int hashCode() {
int result = id;
result = 31 * result + name.hashCode();
result = 31 * result + age;
return result;
}
6. Comparing Lists Ignoring Order
In some cases, the order of elements in the lists is not important. You only need to check if both lists contain the same elements, regardless of their order.
6.1. Using HashSet
for Order-Insensitive Comparison
One way to compare lists ignoring order is to convert them to HashSet
objects. A HashSet
is an unordered collection that does not allow duplicate elements. By converting the lists to HashSet
objects, you can compare their content without considering the order.
6.2. Example: Comparing Lists Ignoring Order Using HashSet
Here’s an example demonstrating how to compare lists ignoring order using HashSet
:
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class OrderInsensitiveListComparison {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("apple");
list1.add("banana");
list1.add("orange");
List<String> list2 = new ArrayList<>();
list2.add("apple");
list2.add("banana");
list2.add("orange");
List<String> list3 = new ArrayList<>();
list3.add("apple");
list3.add("orange");
list3.add("banana");
System.out.println("list1 equals list2 (ignoring order): " + compareListsIgnoringOrder(list1, list2)); // Output: true
System.out.println("list1 equals list3 (ignoring order): " + compareListsIgnoringOrder(list1, list3)); // Output: true
}
public static boolean compareListsIgnoringOrder(List<?> list1, List<?> list2) {
if (list1.size() != list2.size()) {
return false;
}
Set<?> set1 = new HashSet<>(list1);
Set<?> set2 = new HashSet<>(list2);
return set1.equals(set2);
}
}
In this example, the compareListsIgnoringOrder
method converts both lists to HashSet
objects and then compares the HashSet
objects using the equals()
method.
6.3. Using Collections.sort()
for Order-Insensitive Comparison
Another way to compare lists ignoring order is to sort both lists and then compare them using the equals()
method. Sorting the lists ensures that the elements are in the same order, regardless of their original order.
6.4. Example: Comparing Lists Ignoring Order Using Collections.sort()
Here’s an example demonstrating how to compare lists ignoring order using Collections.sort()
:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class OrderInsensitiveListComparison {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("apple");
list1.add("banana");
list1.add("orange");
List<String> list2 = new ArrayList<>();
list2.add("apple");
list2.add("banana");
list2.add("orange");
List<String> list3 = new ArrayList<>();
list3.add("apple");
list3.add("orange");
list3.add("banana");
System.out.println("list1 equals list2 (ignoring order): " + compareListsIgnoringOrder(list1, list2)); // Output: true
System.out.println("list1 equals list3 (ignoring order): " + compareListsIgnoringOrder(list1, list3)); // Output: true
}
public static boolean compareListsIgnoringOrder(List<String> list1, List<String> list2) {
if (list1.size() != list2.size()) {
return false;
}
List<String> sortedList1 = new ArrayList<>(list1);
List<String> sortedList2 = new ArrayList<>(list2);
Collections.sort(sortedList1);
Collections.sort(sortedList2);
return sortedList1.equals(sortedList2);
}
}
In this example, the compareListsIgnoringOrder
method creates sorted copies of both lists using Collections.sort()
and then compares the sorted lists using the equals()
method.
7. Performance Considerations
The performance of list comparison can vary depending on the method used, the size of the lists, and the complexity of the elements. Here are some performance considerations to keep in mind:
7.1. equals()
Method Performance
The equals()
method is generally efficient for small to medium-sized lists. However, for very large lists, the iterative comparison can become time-consuming.
7.2. Iterative Comparison Performance
Iterative comparison can be more efficient than the equals()
method if you can exit the comparison early when a difference is found. However, it can be less efficient if you need to compare all elements.
7.3. hashCode()
Performance
Using hashCode()
as a quick check before using equals()
can improve performance by avoiding unnecessary comparisons. However, the overhead of calculating the hash codes should be considered.
7.4. HashSet
Performance
Converting lists to HashSet
objects for order-insensitive comparison can be efficient for large lists, as the HashSet
provides fast lookups. However, the overhead of creating the HashSet
objects should be considered.
7.5. Collections.sort()
Performance
Sorting lists using Collections.sort()
can be time-consuming, especially for large lists. This method is suitable when the order of elements is not important and you need to compare the content of the lists.
8. Advanced List Comparison Techniques
Beyond the basic methods, there are more advanced techniques for comparing lists, especially when dealing with complex scenarios or large datasets.
8.1. Using Java 8 Streams for Comparison
Java 8 Streams provide a powerful and flexible way to compare lists. You can use streams to filter, map, and compare elements in a declarative and concise manner.
8.1.1. Comparing Lists Using allMatch()
The allMatch()
method can be used to check if all elements in one list satisfy a condition based on the elements in another list.
import java.util.ArrayList;
import java.util.List;
public class StreamListComparison {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
List<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
list2.add(4);
boolean allElementsPresent = list1.stream().allMatch(list2::contains);
System.out.println("All elements of list1 are present in list2: " + allElementsPresent); // Output: true
}
}
8.1.2. Comparing Lists Using anyMatch()
The anyMatch()
method can be used to check if any element in one list satisfies a condition based on the elements in another list.
import java.util.ArrayList;
import java.util.List;
public class StreamListComparison {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
List<Integer> list2 = new ArrayList<>();
list2.add(3);
list2.add(4);
list2.add(5);
boolean anyElementPresent = list1.stream().anyMatch(list2::contains);
System.out.println("Any element of list1 is present in list2: " + anyElementPresent); // Output: true
}
}
8.1.3. Comparing Lists Using noneMatch()
The noneMatch()
method can be used to check if none of the elements in one list satisfy a condition based on the elements in another list.
import java.util.ArrayList;
import java.util.List;
public class StreamListComparison {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
List<Integer> list2 = new ArrayList<>();
list2.add(4);
list2.add(5);
list2.add(6);
boolean noElementPresent = list1.stream().noneMatch(list2::contains);
System.out.println("No element of list1 is present in list2: " + noElementPresent); // Output: true
}
}
8.2. Using Libraries for Advanced Comparison
Several third-party libraries provide advanced features for comparing lists, such as:
- Apache Commons Collections: Provides utility classes for working with collections, including methods for comparing lists.
- Guava: Google’s Guava library offers powerful collection utilities, including methods for comparing lists and identifying differences.
8.2.1. Using Apache Commons Collections
Apache Commons Collections provides the CollectionUtils
class, which offers utility methods for working with collections.
import org.apache.commons.collections4.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
public class ApacheCommonsListComparison {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("apple");
list1.add("banana");
list1.add("orange");
List<String> list2 = new ArrayList<>();
list2.add("apple");
list2.add("banana");
list2.add("orange");
boolean isEqual = CollectionUtils.isEqualCollection(list1, list2);
System.out.println("Lists are equal: " + isEqual); // Output: true
}
}
8.2.2. Using Guava
Guava provides the Lists
class, which offers utility methods for working with lists.
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List;
public class GuavaListComparison {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("apple");
list1.add("banana");
list1.add("orange");
List<String> list2 = new ArrayList<>();
list2.add("apple");
list2.add("banana");
list2.add("orange");
boolean isEqual = Lists.newArrayList(list1).equals(Lists.newArrayList(list2));
System.out.println("Lists are equal: " + isEqual); // Output: true
}
}
8.3. Identifying Differences Between Lists
In many cases, you need to identify the differences between two lists, such as elements that are present in one list but not in the other.
8.3.1. Using removeAll()
to Find Differences
The removeAll()
method can be used to find the elements that are present in one list but not in the other.
import java.util.ArrayList;
import java.util.List;
public class ListDifference {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("apple");
list1.add("banana");
list1.add("orange");
List<String> list2 = new ArrayList<>();
list2.add("banana");
list2.add("orange");
list2.add("grape");
List<String> list1Only = new ArrayList<>(list1);
list1Only.removeAll(list2);
List<String> list2Only = new ArrayList<>(list2);
list2Only.removeAll(list1);
System.out.println("Elements only in list1: " + list1Only); // Output: [apple]
System.out.println("Elements only in list2: " + list2Only); // Output: [grape]
}
}
8.3.2. Using Streams to Find Differences
Java 8 Streams can also be used to find the differences between lists.
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamListDifference {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("apple");
list1.add("banana");
list1.add("orange");
List<String> list2 = new ArrayList<>();
list2.add("banana");
list2.add("orange");
list2.add("grape");
List<String> list1Only = list1.stream()
.filter(element -> !list2.contains(element))
.collect(Collectors.toList());
List<String> list2Only = list2.stream()
.filter(element -> !list1.contains(element))
.collect(Collectors.toList());
System.out.println("Elements only in list1: " + list1Only); // Output: [apple]
System.out.println("Elements only in list2: " + list2Only); // Output: [grape]
}
}
9. Practical Examples of List Comparison
To illustrate the practical applications of list comparison, let’s consider a few real-world examples.
9.1. Comparing Two Lists of User Objects
Suppose you have two lists of User
objects, and you want to check if they contain the same users.
import java.util.ArrayList;
import java.util.List;
public class UserListComparison {
public static void main(String[] args) {
List<User> list1 = new ArrayList<>();
list1.add(new User(1, "John Doe"));
list1.add(new User(2, "Jane Smith"));
List<User> list2 = new ArrayList<>();
list2.add(new User(1, "John Doe"));
list2.add(new User(2, "Jane Smith"));
System.out.println("Lists are equal: " + list1.equals(list2)); // Output: true
}
}
class User {
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
User user = (User) obj;
return id == user.id && name.equals(user.name);
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + name.hashCode();
return result;
}
}
9.2. Comparing Two Lists of Product IDs
Suppose you have two lists of product IDs, and you want to check if they contain the same product IDs, ignoring the order.
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ProductIdListComparison {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
list1.add(101);
list1.add(102);
list1.add(103);
List<Integer> list2 = new ArrayList<>();
list2.add(103);
list2.add(101);
list2.add(102);
System.out.println("Lists are equal (ignoring order): " + compareListsIgnoringOrder(list1, list2)); // Output: true
}
public static boolean compareListsIgnoringOrder(List<?> list1, List<?> list2) {
if (list1.size() != list2.size()) {
return false;
}
Set<?> set1 = new HashSet<>(list1);
Set<?> set2 = new HashSet<>(list2);
return set1.equals(set2);
}
}
9.3. Comparing Two Lists of Transaction Objects
Suppose you have two lists of Transaction
objects, and you want to check if they contain the same transactions, based on their ID and amount.
import java.util.ArrayList;
import java.util.List;
public class TransactionListComparison {
public static void main(String[] args) {
List<Transaction> list1 = new ArrayList<>();
list1.add(new Transaction(1, 100.0));
list1.add(new Transaction(2, 200.0));
List<Transaction> list2 = new ArrayList<>();
list2.add(new Transaction(1, 100.0));
list2.add(new Transaction(2, 200.0));
System.out.println("Lists are equal: " + list1.equals(list2)); // Output: true
}
}
class Transaction {
private int id;
private double amount;
public Transaction(int id, double amount) {
this.id = id;
this.amount = amount;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Transaction transaction = (Transaction) obj;
return id == transaction.id && Double.compare(transaction.amount, amount) == 0;
}
@Override
public int hashCode() {
int result = id;
long temp = Double.doubleToLongBits(amount);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
}
10. Best Practices for List Comparison
To ensure efficient and accurate list comparison, follow these best practices:
10.1. Choose the Right Method
Select the appropriate comparison method based on the specific requirements of your application. Consider factors such as the size of the lists, the importance of element order, and the complexity of the elements.
10.2. Implement equals()
and hashCode()
Correctly
When comparing