How to Implement Comparable in Java: A Comprehensive Guide

The Comparable interface in Java plays a crucial role in enabling object comparison and sorting. At compare.edu.vn, we aim to provide a detailed understanding of how to implement Comparable in Java, ensuring your objects can be easily sorted and compared. This guide covers everything from basic implementation to advanced sorting techniques, offering practical examples and insights to help you master this essential Java feature. Learn about natural ordering, custom comparators, and more, and see how to leverage these LSI keywords effectively in your projects for optimal performance.

1. Understanding the Comparable Interface

The Comparable interface in Java is a fundamental component for enabling objects to be compared with each other. It is part of the java.lang package, meaning it is automatically available in all Java programs without requiring any additional imports. This interface provides a single method, compareTo(), which defines the natural ordering of objects within a class.

1.1 What is the Comparable Interface?

The Comparable interface is used to define a natural order for objects. When a class implements Comparable, it signals that its instances can be compared to each other. This is essential for sorting collections of objects or when you need to determine the relative order of two instances of the same class.

The primary benefit of using Comparable is that it provides a standardized way to compare objects, making it easier to use built-in Java sorting methods like Collections.sort() and Arrays.sort(). Without implementing Comparable, these methods would not know how to order your custom objects.

1.2 Why Use the Comparable Interface?

Implementing the Comparable interface offers several advantages:

  • Natural Ordering: It defines a natural ordering for objects, making it clear how instances of a class should be sorted.
  • Compatibility with Sorting Methods: It allows your custom objects to be sorted using Java’s built-in sorting methods.
  • Ease of Use: It simplifies the process of comparing objects, providing a standard compareTo() method for comparison logic.
  • Integration with Collections: It enables seamless integration with Java’s collections framework, allowing you to easily sort lists, sets, and other collections of your custom objects.

1.3 Structure of the Comparable Interface

The Comparable interface has a simple structure, consisting of a single method:

public interface Comparable<T> {
    int compareTo(T o);
}

Here:

  • <T>: This is a generic type parameter that specifies the type of object that the class will be compared to. It ensures type safety by allowing comparisons only between objects of the same class.

  • compareTo(T o): This method compares the current object to the object o passed as an argument. It returns an integer value that indicates the relative order of the two objects.

    • A negative value if the current object is less than the argument object.
    • A positive value if the current object is greater than the argument object.
    • Zero if the current object is equal to the argument object.

2. Implementing the Comparable Interface

To implement the Comparable interface, you need to follow a few key steps. This section provides a detailed guide on how to properly implement Comparable in your classes, ensuring correct and efficient object comparison.

2.1 Steps to Implement Comparable

  1. Declare that the Class Implements Comparable:

    First, modify your class declaration to include the implements keyword followed by the Comparable interface and the class type within angle brackets (<>). For example, if you have a class named Person, the declaration would look like this:

    public class Person implements Comparable<Person> {
        // Class members and methods
    }
  2. Override the compareTo() Method:

    Next, you need to override the compareTo() method in your class. This method will contain the logic for comparing two instances of your class. The method signature must match the one defined in the Comparable interface:

    @Override
    public int compareTo(Person other) {
        // Comparison logic
    }
  3. Implement the Comparison Logic:

    Inside the compareTo() method, implement the logic to compare the current object (this) with the other object. The comparison should be based on one or more attributes of the class. The method must return:

    • A negative integer if this object is less than the other object.
    • A positive integer if this object is greater than the other object.
    • Zero if this object is equal to the other object.

2.2 Example: Implementing Comparable in a Person Class

Let’s consider a Person class with attributes such as name and age. We want to compare Person objects based on their age. Here’s how you can implement the Comparable interface:

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
               "name='" + name + ''' +
               ", age=" + age +
               '}';
    }

    @Override
    public int compareTo(Person other) {
        // Compare based on age
        return Integer.compare(this.age, other.age);
    }

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));

        Collections.sort(people);

        for (Person person : people) {
            System.out.println(person);
        }
    }
}

In this example:

  • The Person class implements Comparable<Person>.
  • The compareTo() method compares the ages of two Person objects using Integer.compare(), which returns -1, 0, or 1 based on whether the first age is less than, equal to, or greater than the second age.
  • The main() method creates a list of Person objects, sorts them using Collections.sort(), and prints the sorted list.

2.3 Implementing Comparable with Multiple Fields

Sometimes, you may need to compare objects based on multiple fields. For example, if two Person objects have the same age, you might want to compare them based on their name. Here’s how you can modify the compareTo() method to handle multiple fields:

@Override
public int compareTo(Person other) {
    // Compare based on age first
    int ageComparison = Integer.compare(this.age, other.age);

    // If ages are the same, compare based on name
    if (ageComparison == 0) {
        return this.name.compareTo(other.name);
    }

    return ageComparison;
}

In this modified compareTo() method:

  • First, the ages are compared. If the ages are different, the result of this comparison is returned.
  • If the ages are the same (ageComparison == 0), the names are compared using the compareTo() method of the String class. This ensures that Person objects with the same age are sorted alphabetically by name.

2.4 Best Practices for Implementing Comparable

  • Consistency: Ensure that your compareTo() method is consistent with the equals() method. If two objects are equal according to equals(), their compareTo() method should return 0.
  • Type Safety: Use the generic type parameter <T> to ensure that you are only comparing objects of the same class.
  • Null Handling: Be mindful of null values. If a field can be null, handle the null case appropriately to avoid NullPointerException.
  • Clarity: Write clear and concise comparison logic. Use helper methods like Integer.compare() and String.compareTo() to simplify the code.
  • Reflexivity, Symmetry, and Transitivity: Ensure that your compareTo method adheres to the rules of reflexivity (x.compareTo(x) == 0), symmetry (if x.compareTo(y) returns the same sign as y.compareTo(x), then x == y), and transitivity (if x.compareTo(y) > 0 and y.compareTo(z) > 0, then x.compareTo(z) > 0).

3. Advanced Sorting Techniques

While implementing the Comparable interface is useful for defining a natural order for objects, there are situations where you need more flexibility in how objects are sorted. This section explores advanced sorting techniques using Comparator and lambda expressions, providing you with powerful tools to customize object sorting.

3.1 Using the Comparator Interface

The Comparator interface is a functional interface that allows you to define custom sorting logic without modifying the class of the objects being sorted. This is particularly useful when you need to sort objects in multiple ways or when you don’t have control over the class definition.

3.1.1 Creating a Comparator

To create a Comparator, you need to implement the compare() method, which takes two objects as arguments and returns an integer value indicating their relative order.

import java.util.Comparator;

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
               "name='" + name + ''' +
               ", age=" + age +
               '}';
    }

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));

        // Create a Comparator to sort by name
        Comparator<Person> nameComparator = new Comparator<Person>() {
            @Override
            public int compare(Person p1, Person p2) {
                return p1.getName().compareTo(p2.getName());
            }
        };

        Collections.sort(people, nameComparator);

        for (Person person : people) {
            System.out.println(person);
        }
    }
}

In this example:

  • A Comparator<Person> named nameComparator is created to compare Person objects based on their names.
  • The compare() method uses the compareTo() method of the String class to compare the names.
  • Collections.sort() is used with the nameComparator to sort the list of Person objects by name.

3.1.2 Using Lambda Expressions with Comparator

With Java 8 and later, you can use lambda expressions to create Comparator instances more concisely.

import java.util.Comparator;

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
               "name='" + name + ''' +
               ", age=" + age +
               '}';
    }

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));

        // Create a Comparator to sort by name using a lambda expression
        Comparator<Person> nameComparator = (p1, p2) -> p1.getName().compareTo(p2.getName());

        Collections.sort(people, nameComparator);

        for (Person person : people) {
            System.out.println(person);
        }
    }
}

This example demonstrates how to create a Comparator using a lambda expression, making the code more readable and compact.

3.2 Chaining Comparators

You can chain multiple Comparator instances to sort objects based on multiple criteria. This is achieved using the thenComparing() method of the Comparator interface.

import java.util.Comparator;

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
               "name='" + name + ''' +
               ", age=" + age +
               '}';
    }

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));
        people.add(new Person("Alice", 25));

        // Create a Comparator to sort by name and then by age
        Comparator<Person> chainedComparator = Comparator.comparing(Person::getName)
                .thenComparing(Person::getAge);

        Collections.sort(people, chainedComparator);

        for (Person person : people) {
            System.out.println(person);
        }
    }
}

In this example:

  • The comparing() method is used to create a Comparator that sorts Person objects by name.
  • The thenComparing() method is chained to sort objects with the same name by age.
  • The Person::getName and Person::getAge are method references that provide the comparing and thenComparing methods with the values to compare.

3.3 Sorting with Streams

Java Streams provide a convenient way to sort collections using the sorted() method. This method can be used with or without a Comparator.

3.3.1 Sorting with Natural Order

If the objects in the stream implement the Comparable interface, you can use the sorted() method without any arguments to sort the stream in natural order.

import java.util.Comparator;
import java.util.stream.Collectors;

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
               "name='" + name + ''' +
               ", age=" + age +
               '}';
    }

    @Override
    public int compareTo(Person other) {
        return Integer.compare(this.age, other.age);
    }

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));

        // Sort the stream using natural order
        List<Person> sortedPeople = people.stream()
                .sorted()
                .collect(Collectors.toList());

        for (Person person : sortedPeople) {
            System.out.println(person);
        }
    }
}

In this example:

  • The Person class implements Comparable<Person>, providing a natural order based on age.
  • The sorted() method is used without any arguments to sort the stream of Person objects in natural order.
  • The collect(Collectors.toList()) method is used to collect the sorted elements into a new list.

3.3.2 Sorting with a Comparator

You can also use the sorted() method with a Comparator to sort the stream using custom sorting logic.

import java.util.Comparator;
import java.util.stream.Collectors;

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
               "name='" + name + ''' +
               ", age=" + age +
               '}';
    }

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));

        // Sort the stream using a Comparator
        List<Person> sortedPeople = people.stream()
                .sorted(Comparator.comparing(Person::getName))
                .collect(Collectors.toList());

        for (Person person : sortedPeople) {
            System.out.println(person);
        }
    }
}

In this example:

  • The sorted() method is used with Comparator.comparing(Person::getName) to sort the stream of Person objects by name.

3.4 Common Use Cases for Advanced Sorting

  • Sorting Data from External Sources: When dealing with data from databases, APIs, or other external sources, you often need to sort objects based on criteria that are not part of their natural order.
  • Implementing Custom Sorting Algorithms: Advanced sorting techniques allow you to implement custom sorting algorithms tailored to specific use cases.
  • Dynamic Sorting: You can dynamically change the sorting criteria at runtime based on user input or other factors.
  • Optimizing Sorting Performance: By carefully choosing the appropriate sorting technique, you can optimize the performance of your sorting operations.

4. Best Practices and Considerations

Implementing the Comparable interface and using Comparator effectively requires careful consideration to ensure your sorting logic is robust, efficient, and maintainable. This section outlines best practices and important considerations to keep in mind.

4.1 Consistency with equals() Method

A critical best practice is to ensure that your compareTo() method is consistent with the equals() method. If two objects are equal according to equals(), their compareTo() method should return 0. This consistency is important for maintaining the integrity of sorted collections and ensuring predictable behavior.

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
               "name='" + name + ''' +
               ", age=" + age +
               '}';
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public int compareTo(Person other) {
        // Compare based on age first
        int ageComparison = Integer.compare(this.age, other.age);

        // If ages are the same, compare based on name
        if (ageComparison == 0) {
            return this.name.compareTo(other.name);
        }

        return ageComparison;
    }
}

In this example:

  • The equals() method checks if two Person objects have the same name and age.
  • The compareTo() method compares Person objects based on age first and then name.
  • If two Person objects are equal according to equals(), their compareTo() method will return 0.

4.2 Handling Null Values

When comparing objects, you need to be mindful of null values. If a field can be null, you should handle the null case appropriately to avoid NullPointerException.

import java.util.Objects;

public class Person implements Comparable<Person> {
    private String name;
    private Integer age; // Changed to Integer to allow null values

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
               "name='" + name + ''' +
               ", age=" + age +
               '}';
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return Objects.equals(age, person.age) && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public int compareTo(Person other) {
        // Handle null age values
        if (this.age == null && other.age == null) {
            return 0; // Both ages are null, consider them equal
        } else if (this.age == null) {
            return -1; // This person's age is null, consider it less
        } else if (other.age == null) {
            return 1; // Other person's age is null, consider it greater
        }

        // Compare based on age
        int ageComparison = Integer.compare(this.age, other.age);

        // If ages are the same, compare based on name
        if (ageComparison == 0) {
            if (this.name == null && other.name == null) {
                return 0; // Both names are null, consider them equal
            } else if (this.name == null) {
                return -1; // This person's name is null, consider it less
            } else if (other.name == null) {
                return 1; // Other person's name is null, consider it greater
            }
            return this.name.compareTo(other.name);
        }

        return ageComparison;
    }
}

In this example:

  • The age field is changed to Integer to allow null values.
  • The compareTo() method includes null checks for both age and name.
  • Null values are handled in a way that ensures consistency and avoids NullPointerException.

4.3 Performance Considerations

Sorting can be a performance-intensive operation, especially when dealing with large collections. Here are some performance considerations to keep in mind:

  • Choose the Right Sorting Algorithm: Java’s Collections.sort() method uses a highly optimized sorting algorithm (typically a variant of merge sort or quicksort). However, for specialized use cases, you might consider using different sorting algorithms.
  • Minimize Object Creation: Avoid creating unnecessary objects within the compareTo() or compare() methods, as this can impact performance.
  • Use Primitive Types: When possible, use primitive types (e.g., int, double) instead of wrapper objects (e.g., Integer, Double) for comparison, as primitive types are more efficient.
  • Cache Comparison Results: If you are performing multiple comparisons on the same objects, consider caching the results of previous comparisons to avoid redundant computations.

4.4 Immutability

If your objects are immutable (i.e., their state cannot be changed after creation), you can safely cache the results of the compareTo() method, as the comparison result will always be the same.

public final class ImmutablePerson implements Comparable<ImmutablePerson> {
    private final String name;
    private final int age;
    private final int comparisonResult;

    public ImmutablePerson(String name, int age) {
        this.name = name;
        this.age = age;
        this.comparisonResult = calculateComparisonResult();
    }

    private int calculateComparisonResult() {
        int ageComparison = Integer.compare(this.age, 0);
        if (ageComparison == 0) {
            return this.name.compareTo("");
        }
        return ageComparison;
    }

    @Override
    public int compareTo(ImmutablePerson other) {
        return this.comparisonResult;
    }
}

In this example:

  • The ImmutablePerson class is declared as final to prevent subclassing.
  • The name and age fields are declared as final to ensure immutability.
  • The comparisonResult field stores the result of the compareTo() method, which is calculated only once during object creation.
  • The compareTo() method simply returns the cached comparisonResult, avoiding redundant computations.

4.5 Testing Your Comparison Logic

It is essential to thoroughly test your comparison logic to ensure that it is correct and consistent. Here are some testing strategies to consider:

  • Unit Tests: Write unit tests to verify that the compareTo() or compare() methods return the correct results for different input scenarios.
  • Edge Cases: Test edge cases, such as null values, empty strings, and extreme values.
  • Randomized Tests: Use randomized tests to generate a large number of random objects and verify that the sorting logic produces the expected results.
  • Performance Tests: Conduct performance tests to measure the performance of your sorting logic and identify potential bottlenecks.

5. Real-World Examples

To illustrate the practical application of the Comparable interface and Comparator, this section provides real-world examples across various domains.

5.1 Sorting a List of Products by Price and Name

Consider an e-commerce application where you need to display a list of products sorted by price and name. The Product class might look like this:

import java.util.Comparator;

public class Product {
    private String name;
    private double price;

    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "Product{" +
               "name='" + name + ''' +
               ", price=" + price +
               '}';
    }

    public static Comparator<Product> priceAndNameComparator = Comparator.comparing(Product::getPrice)
            .thenComparing(Product::getName);
}

In this example:

  • The Product class has name and price attributes.
  • A Comparator<Product> named priceAndNameComparator is created to sort products by price and then by name.

You can then use this Comparator to sort a list of Product objects:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Product> products = new ArrayList<>();
        products.add(new Product("Laptop", 1200.0));
        products.add(new Product("Tablet", 300.0));
        products.add(new Product("Phone", 800.0));
        products.add(new Product("Headphones", 100.0));
        products.add(new Product("Charger", 25.0));

        Collections.sort(products, Product.priceAndNameComparator);

        for (Product product : products) {
            System.out.println(product);
        }
    }
}

5.2 Sorting a List of Employees by Salary and Seniority

In a human resources application, you might need to sort a list of employees by salary and seniority. The Employee class could be structured as follows:

import java.time.LocalDate;
import java.util.Comparator;

public class Employee {
    private String name;
    private double salary;
    private LocalDate hireDate;

    public Employee(String name, double salary, LocalDate hireDate) {
        this.name = name;
        this.salary = salary;
        this.hireDate = hireDate;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public LocalDate getHireDate() {
        return hireDate;
    }

    @Override
    public String toString() {
        return "Employee{" +
               "name='" + name + ''' +
               ", salary=" + salary +
               ", hireDate=" + hireDate +
               '}';
    }

    public static Comparator<Employee> salaryAndSeniorityComparator = Comparator.comparing(Employee::getSalary)
            .thenComparing(Employee::getHireDate);
}

In this example:

  • The Employee class has name, salary, and hireDate attributes.
  • A Comparator<Employee> named salaryAndSeniorityComparator is created to sort employees by salary and then by hire date (seniority).

You can then use this Comparator to sort a list of Employee objects:

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee("Alice", 60000.0, LocalDate.of(2020, 1, 1)));
        employees.add(new Employee("Bob", 50000.0, LocalDate.of(2021, 2, 15)));
        employees.add(new Employee("Charlie", 70000.0, LocalDate.of(2019, 5, 10)));
        employees.add(new Employee("David", 60000.0, LocalDate.of(2020, 6, 20)));

        Collections.sort(employees, Employee.salaryAndSeniorityComparator);

        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }
}

5.3 Sorting a List of Students by GPA and Name

In an academic application, you might need to sort a list of students by GPA and name. The Student class could be structured as follows:

import java.util.Comparator;

public class Student {
    private String name;
    private double gpa;

    public Student(String name, double gpa) {
        this.name = name;
        this.gpa = gpa;
    }

    public String getName() {
        return name;
    }

    public double getGpa() {
        return gpa;
    }

    @Override
    public String toString() {
        return "Student{" +
               "name='" + name + ''' +
               ", gpa=" + gpa +
               '}';
    }

    public static Comparator<Student> gpaAndNameComparator = Comparator.comparing(Student::getGpa)
            .reversed() // Sort GPA in descending order
            .thenComparing(Student::getName);
}

In this example:

  • The Student class has name and gpa attributes.
  • A Comparator<Student> named gpaAndNameComparator is created to sort students by GPA (in descending order) and then by name.

You can then use this Comparator to sort a list of Student objects:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Alice", 3.8));
        students.add(new Student("Bob", 3.5));
        students.add(new Student("Charlie", 4.0));
        students.add(new Student("David", 3.8));

        Collections.sort(students, Student.gpaAndNameComparator);

        for (Student student : students) {
            System.out.println(student);
        }
    }
}

6. Common Mistakes to Avoid

Implementing the Comparable interface and using Comparator can be tricky, and there are several common mistakes that developers often make. This section highlights these mistakes and provides guidance on how to avoid them.

6.1 Not Maintaining Consistency with equals()

One of the most common mistakes is not maintaining consistency between the compareTo() method and the equals() method. If two objects are equal according to equals(), their compareTo() method should return 0. Failure to maintain this consistency can lead to unexpected behavior when using sorted collections.

Example of Inconsistency:

public class Product implements Comparable<Product> {
    private String name;
    private double price;

    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Product product = (Product) obj;
        return Double.compare(product.price, price) == 0; // Only compare price
    }

    @Override
    public int hashCode() {
        return Objects.hash(price); // Only hash price
    }

    @Override
    public int compareTo(Product other) {
        return this.name.compareTo(other.name); // Compare by name
    }
}

In this example, the equals() method compares Product objects based

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *