Comparable In Java defines a natural ordering for objects, enhancing sorting and data structure utilization. COMPARE.EDU.VN offers comprehensive comparisons to help you understand its applications. This guide explores Java’s Comparable interface, elucidating its purpose, implementation, and advantages.
1. Understanding the Comparable Interface in Java
The Comparable
interface in Java is a cornerstone for establishing a natural ordering among objects of a class. By implementing this interface, a class dictates how its instances should be compared to one another, enabling seamless sorting and utilization within sorted data structures. This section delves into the specifics of the Comparable
interface, exploring its purpose, method signature, and the concept of natural ordering.
1.1. Definition and Purpose
The Comparable
interface, found in the java.lang
package, is designed to provide a mechanism for comparing objects of a class. It imposes a total ordering on the objects, allowing them to be sorted in a specific order. This ordering is termed the class’s natural ordering. The primary purpose of this interface is to enable classes to define a default comparison logic that can be used by sorting algorithms and sorted collections.
1.2. Method Signature: compareTo(T o)
The Comparable
interface contains a single method, compareTo(T o)
, which plays a pivotal role in determining the order of objects. The method signature is as follows:
int compareTo(T o);
T
: Represents the type of the object being compared.o
: The object to be compared with the instance on which the method is called.Returns
:- A negative integer if the current object is less than the specified object.
- Zero if the current object is equal to the specified object.
- A positive integer if the current object is greater than the specified object.
This method essentially defines the logic for comparing two objects. When an object a
calls a.compareTo(b)
, it compares itself (a
) with the object b
. The return value indicates the relative order of a
and b
.
1.3. Natural Ordering: What Does It Mean?
Natural ordering refers to the default ordering of objects of a class that implements the Comparable
interface. It is the inherent way in which objects of that class are sorted. This ordering is “natural” because it is defined within the class itself, representing the most logical or common way to compare instances of that class.
For example, the Integer
class in Java implements Comparable<Integer>
, and its natural ordering is numerical order. Similarly, the String
class implements Comparable<String>
, and its natural ordering is lexicographical (dictionary) order.
1.4. Consistency with equals()
It is strongly recommended that the natural ordering of a class be consistent with equals. This means that if two objects are equal according to the equals()
method, their compareTo()
method should return 0. Formally, for any two objects e1
and e2
of class C
, e1.compareTo(e2) == 0
should have the same boolean value as e1.equals(e2)
.
While not mandatory, adhering to this principle ensures that sorted sets and sorted maps behave predictably. If the natural ordering is inconsistent with equals, these data structures may violate the general contract for sets and maps, leading to unexpected behavior. For instance, adding two keys a
and b
to a sorted set where !a.equals(b)
but a.compareTo(b) == 0
will result in the second add
operation returning false
, as the sorted set considers a
and b
equivalent.
1.5. Benefits of Using Comparable
Implementing the Comparable
interface offers several key advantages:
- Automatic Sorting: Objects of classes that implement
Comparable
can be automatically sorted using methods likeCollections.sort()
andArrays.sort()
. - Sorted Data Structures: These objects can be used as keys in
SortedMap
or elements inSortedSet
without needing an externalComparator
. - Default Comparison Logic: It provides a default comparison logic that is inherent to the class, making it clear how objects of that class should be compared.
- Code Readability: It enhances code readability by providing a standardized way to compare objects.
1.6. Example Scenario
Consider a Student
class with attributes like name
and age
. By implementing Comparable<Student>
, you can define the natural ordering based on age. This allows you to easily sort a list of students by their age using Collections.sort()
.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Student implements Comparable<Student> {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student other) {
return Integer.compare(this.age, other.age);
}
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + '}';
}
}
public class ComparableExample {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 20));
students.add(new Student("Bob", 22));
students.add(new Student("Charlie", 19));
Collections.sort(students);
for (Student student : students) {
System.out.println(student);
}
}
}
In this example, the compareTo()
method compares students based on their age. The Collections.sort()
method uses this natural ordering to sort the list of students.
1.7. Conclusion
The Comparable
interface is a fundamental part of Java’s collections framework, providing a standardized way to define the natural ordering of objects. By implementing this interface, classes can specify how their instances should be compared, enabling seamless sorting and utilization in sorted data structures. Understanding and correctly implementing Comparable
is essential for writing efficient and maintainable Java code. For more detailed comparisons and insights, visit COMPARE.EDU.VN.
2. Implementing the Comparable Interface: A Step-by-Step Guide
Implementing the Comparable
interface in Java involves defining a natural ordering for objects of a class. This ordering is crucial for sorting and using these objects in sorted collections. This section provides a detailed, step-by-step guide on how to implement the Comparable
interface, complete with examples and best practices.
2.1. Step 1: Implement the Comparable
Interface
The first step is to declare that your class implements the Comparable
interface. This involves adding implements Comparable<YourClass>
to your class declaration.
class MyClass implements Comparable<MyClass> {
// Class members and methods
}
Here, MyClass
is the class for which you want to define a natural ordering. By implementing Comparable<MyClass>
, you are indicating that objects of MyClass
can be compared to each other.
2.2. Step 2: Override the compareTo()
Method
The next step is to override the compareTo(T o)
method in your class. This method defines the logic for comparing two objects of your class.
class MyClass implements Comparable<MyClass> {
private int value;
public MyClass(int value) {
this.value = value;
}
@Override
public int compareTo(MyClass other) {
// Comparison logic here
return Integer.compare(this.value, other.value);
}
}
In this example, MyClass
has an integer field value
. The compareTo()
method compares the value
of the current object with the value
of the other
object. The Integer.compare()
method is used to perform the comparison, ensuring correct handling of integer values.
2.3. Step 3: Define the Comparison Logic
Inside the compareTo()
method, you need to define the logic for comparing objects. The return value should follow these rules:
- Return a negative integer if the current object is less than the other object.
- Return zero if the current object is equal to the other object.
- Return a positive integer if the current object is greater than the other object.
Consider the following example with a Person
class, where the natural ordering is based on age and then name:
class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person other) {
int ageComparison = Integer.compare(this.age, other.age);
if (ageComparison != 0) {
return ageComparison;
}
return this.name.compareTo(other.name);
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + '}';
}
}
In this case, the compareTo()
method first compares the ages of the two Person
objects. If the ages are different, it returns the result of the age comparison. If the ages are the same, it compares the names lexicographically using the String.compareTo()
method.
2.4. Step 4: Ensure Consistency with equals()
It is highly recommended to ensure that your compareTo()
method is consistent with the equals()
method. This means that if a.equals(b)
is true, then a.compareTo(b)
should return 0. If these methods are inconsistent, sorted collections may behave unexpectedly.
class Product implements Comparable<Product> {
private String id;
private String name;
private double price;
public Product(String id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
@Override
public int compareTo(Product other) {
return this.id.compareTo(other.id);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Product product = (Product) obj;
return id.equals(product.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public String toString() {
return "Product{id='" + id + "', name='" + name + "', price=" + price + '}';
}
}
In this example, the Product
class defines compareTo()
based on the id
field and equals()
also based on the id
field. This ensures that the natural ordering is consistent with equality.
2.5. Best Practices
- Use Established Comparison Methods: Utilize methods like
Integer.compare()
,Double.compare()
, andString.compareTo()
for comparing primitive types and strings to avoid common pitfalls. - Handle Null Values: Be mindful of null values and handle them appropriately. A common approach is to throw a
NullPointerException
ifnull
is encountered, as specified in theComparable
contract. - Consider Multiple Fields: If your class has multiple fields, consider the order in which they should be compared. Start with the most significant field and proceed to less significant fields only if the more significant fields are equal.
- Test Thoroughly: Ensure your
compareTo()
method is thoroughly tested with various inputs to confirm it provides the correct ordering.
2.6. Example Scenario
Consider a Book
class with attributes like title
, author
, and publicationYear
. You can implement Comparable<Book>
to define the natural ordering based on publication year and then title.
class Book implements Comparable<Book> {
private String title;
private String author;
private int publicationYear;
public Book(String title, String author, int publicationYear) {
this.title = title;
this.author = author;
this.publicationYear = publicationYear;
}
@Override
public int compareTo(Book other) {
int yearComparison = Integer.compare(this.publicationYear, other.publicationYear);
if (yearComparison != 0) {
return yearComparison;
}
return this.title.compareTo(other.title);
}
@Override
public String toString() {
return "Book{title='" + title + "', author='" + author + "', publicationYear=" + publicationYear + '}';
}
}
In this example, the compareTo()
method first compares the publication years of the two Book
objects. If the publication years are different, it returns the result of the year comparison. If the publication years are the same, it compares the titles lexicographically.
2.7. Conclusion
Implementing the Comparable
interface in Java is a straightforward process that involves implementing the Comparable
interface, overriding the compareTo()
method, defining the comparison logic, and ensuring consistency with the equals()
method. By following these steps and best practices, you can effectively define a natural ordering for your objects, enabling seamless sorting and utilization in sorted data structures. For more comprehensive guides and comparisons, visit COMPARE.EDU.VN.
3. Comparable vs. Comparator: Choosing the Right Interface for Sorting
In Java, both Comparable
and Comparator
interfaces are used to define the order of objects, but they serve different purposes and are used in different scenarios. Understanding the differences between these two interfaces is crucial for effective sorting and ordering of objects. This section provides a detailed comparison of Comparable
and Comparator
, helping you choose the right interface for your sorting needs.
3.1. Key Differences
Feature | Comparable | Comparator |
---|---|---|
Definition | Defines a natural ordering for a class. | Defines an ordering for objects of a class, which may or may not be the natural ordering. |
Interface | java.lang.Comparable |
java.util.Comparator |
Method | compareTo(T o) |
compare(T o1, T o2) |
Implementation | Implemented by the class whose objects are to be compared. | Implemented by a separate class or using an anonymous class. |
Number of Orders | Allows only one natural ordering per class. | Allows multiple different orderings for the same class. |
Modification | Requires modification of the class to implement the interface. | Does not require modification of the class; ordering can be defined externally. |
Use Case | Use when the class has a single, obvious way to compare its objects (e.g., sorting integers numerically, strings lexicographically). | Use when you need multiple ways to sort objects of a class, or when you don’t have control over the class’s source code (e.g., sorting by different fields, sorting in reverse order). |
Example | Integer , String , and Date classes implement Comparable to define their natural orderings. |
Sorting a list of Employee objects by salary, name, or department using separate Comparator implementations. |
3.2. Comparable: Natural Ordering
The Comparable
interface is used to define a natural ordering for a class. When a class implements Comparable
, it specifies how its objects should be compared to each other by default. This is particularly useful when there is a single, obvious way to compare objects of that class.
3.2.1. Example: Sorting a List of Integers
The Integer
class implements Comparable<Integer>
, defining its natural ordering as numerical order. This allows you to easily sort a list of integers using Collections.sort()
.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ComparableExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(5);
numbers.add(2);
numbers.add(8);
numbers.add(1);
Collections.sort(numbers);
System.out.println(numbers); // Output: [1, 2, 5, 8]
}
}
In this example, Collections.sort()
uses the natural ordering defined by the Integer
class to sort the list of integers in ascending order.
3.2.2. When to Use Comparable
- When you want to define a default ordering for objects of a class.
- When there is a single, obvious way to compare objects of that class.
- When you have control over the class’s source code and can modify it.
3.3. Comparator: Custom Ordering
The Comparator
interface is used to define custom orderings for objects of a class. Unlike Comparable
, Comparator
is implemented by a separate class or using an anonymous class, allowing you to define multiple different orderings for the same class without modifying the class itself.
3.3.1. Example: Sorting a List of Employees by Salary
Consider an Employee
class with attributes like name
and salary
. You can define a Comparator
to sort a list of employees by their salary.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
@Override
public String toString() {
return "Employee{name='" + name + "', salary=" + salary + '}';
}
}
public class ComparatorExample {
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("Alice", 50000));
employees.add(new Employee("Bob", 60000));
employees.add(new Employee("Charlie", 40000));
Comparator<Employee> salaryComparator = Comparator.comparingDouble(Employee::getSalary);
Collections.sort(employees, salaryComparator);
for (Employee employee : employees) {
System.out.println(employee);
}
}
}
In this example, a Comparator
is defined using Comparator.comparingDouble()
to compare employees based on their salary. Collections.sort()
then uses this Comparator
to sort the list of employees by salary in ascending order.
3.3.2. When to Use Comparator
- When you need multiple ways to sort objects of a class.
- When you don’t have control over the class’s source code and cannot modify it.
- When you want to define a custom ordering that is different from the natural ordering.
3.4. Combining Comparable
and Comparator
In some cases, you might want to use both Comparable
and Comparator
together. For example, you might define a natural ordering for a class using Comparable
, but also provide additional custom orderings using Comparator
.
3.4.1. Example: Sorting a List of Students by Name and Age
Consider a Student
class that implements Comparable
based on age. You can also define a Comparator
to sort students by name.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Student implements Comparable<Student> {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public int compareTo(Student other) {
return Integer.compare(this.age, other.age);
}
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + '}';
}
}
public class CombiningExample {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 20));
students.add(new Student("Bob", 22));
students.add(new Student("Charlie", 19));
// Sort by age (natural ordering)
Collections.sort(students);
System.out.println("Sorted by age: " + students);
// Sort by name using a Comparator
Comparator<Student> nameComparator = Comparator.comparing(Student::getName);
Collections.sort(students, nameComparator);
System.out.println("Sorted by name: " + students);
}
}
In this example, the Student
class implements Comparable
to define the natural ordering based on age. A Comparator
is also defined to sort students by name. This allows you to sort the list of students by either age or name, depending on your needs.
3.5. Choosing the Right Interface
When deciding whether to use Comparable
or Comparator
, consider the following questions:
- Is there a single, obvious way to compare objects of the class? If yes, use
Comparable
. - Do you need multiple ways to sort objects of the class? If yes, use
Comparator
. - Do you have control over the class’s source code? If yes, you can use either
Comparable
orComparator
. If no, useComparator
.
3.6. Conclusion
Comparable
and Comparator
are both essential interfaces for defining the order of objects in Java. Comparable
defines a natural ordering for a class, while Comparator
allows you to define custom orderings without modifying the class itself. Understanding the differences between these two interfaces and when to use each one is crucial for effective sorting and ordering of objects. For more detailed comparisons and insights, visit COMPARE.EDU.VN.
4. Practical Examples of Comparable in Java
The Comparable
interface in Java is widely used to define a natural ordering for objects of various classes. This section provides practical examples of how to implement the Comparable
interface in different scenarios, showcasing its versatility and utility.
4.1. Example 1: Sorting a List of Products by Price
Consider a Product
class with attributes like id
, name
, and price
. You can implement Comparable<Product>
to define the natural ordering based on the price.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Product implements Comparable<Product> {
private String id;
private String name;
private double price;
public Product(String id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
@Override
public int compareTo(Product other) {
return Double.compare(this.price, other.price);
}
@Override
public String toString() {
return "Product{id='" + id + "', name='" + name + "', price=" + price + '}';
}
}
public class ProductComparableExample {
public static void main(String[] args) {
List<Product> products = new ArrayList<>();
products.add(new Product("101", "Laptop", 1200.00));
products.add(new Product("102", "Keyboard", 75.00));
products.add(new Product("103", "Mouse", 25.00));
Collections.sort(products);
for (Product product : products) {
System.out.println(product);
}
}
}
In this example, the compareTo()
method compares products based on their price. The Collections.sort()
method uses this natural ordering to sort the list of products in ascending order of price.
Alt: Sorted list of products by price using Comparable interface in Java
4.2. Example 2: Sorting a List of Dates
The java.util.Date
class implements Comparable<Date>
, defining its natural ordering as chronological order. This allows you to easily sort a list of dates using Collections.sort()
.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class DateComparableExample {
public static void main(String[] args) {
List<Date> dates = new ArrayList<>();
dates.add(new Date(2023, 0, 1)); // January 1, 3923
dates.add(new Date(2023, 5, 15)); // June 15, 3923
dates.add(new Date(2023, 2, 20)); // March 20, 3923
Collections.sort(dates);
for (Date date : dates) {
System.out.println(date);
}
}
}
In this example, Collections.sort()
uses the natural ordering defined by the Date
class to sort the list of dates in chronological order.
4.3. Example 3: Sorting a List of Strings Case-Insensitively
The String
class implements Comparable<String>
, defining its natural ordering as lexicographical order. However, you can create a custom class that wraps a String and implements Comparable
to sort strings case-insensitively.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class CaseInsensitiveString implements Comparable<CaseInsensitiveString> {
private String value;
public CaseInsensitiveString(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public int compareTo(CaseInsensitiveString other) {
return this.value.compareToIgnoreCase(other.value);
}
@Override
public String toString() {
return value;
}
}
public class StringComparableExample {
public static void main(String[] args) {
List<CaseInsensitiveString> strings = new ArrayList<>();
strings.add(new CaseInsensitiveString("Apple"));
strings.add(new CaseInsensitiveString("banana"));
strings.add(new CaseInsensitiveString("Orange"));
Collections.sort(strings);
for (CaseInsensitiveString str : strings) {
System.out.println(str);
}
}
}
In this example, the CaseInsensitiveString
class implements Comparable
and compares strings case-insensitively using the compareToIgnoreCase()
method.
Alt: Sorting list of strings case-insensitively using Comparable interface in Java
4.4. Example 4: Sorting a List of Custom Objects with Multiple Criteria
Consider a Employee
class with attributes like name
, salary
, and department
. You can implement Comparable<Employee>
to define the natural ordering based on multiple criteria, such as salary (descending) and then name (ascending).
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Employee implements Comparable<Employee> {
private String name;
private double salary;
private String department;
public Employee(String name, double salary, String department) {
this.name = name;
this.salary = salary;
this.department = department;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public String getDepartment() {
return department;
}
@Override
public int compareTo(Employee other) {
int salaryComparison = Double.compare(other.salary, this.salary); // Descending order of salary
if (salaryComparison != 0) {
return salaryComparison;
}
return this.name.compareTo(other.name); // Ascending order of name
}
@Override
public String toString() {
return "Employee{name='" + name + "', salary=" + salary + "', department='" + department + '}';
}
}
public class EmployeeComparableExample {
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("Alice", 50000, "IT"));
employees.add(new Employee("Bob", 60000, "HR"));
employees.add(new Employee("Charlie", 50000, "IT"));
Collections.sort(employees);
for (Employee employee : employees) {
System.out.println(employee);
}
}
}
In this example, the compareTo()
method compares employees based on salary (in descending order) and then name (in ascending order). This allows you to sort the list of employees primarily by salary (highest to lowest) and then alphabetically by name for employees with the same salary.
4.5. Conclusion
These practical examples demonstrate the versatility of the Comparable
interface in Java. By implementing Comparable
, you can define a natural ordering for objects of various classes, enabling seamless sorting and utilization in sorted data structures. Whether you are sorting products by price, dates chronologically, strings case-insensitively, or custom objects with multiple criteria, Comparable
provides a powerful and flexible way to define the order of your objects. For more comprehensive guides and comparisons, visit compare.edu.vn.
5. Advanced Usage of Comparable: Beyond Basic Sorting
While the Comparable
interface is primarily known for enabling basic sorting of objects, its capabilities extend beyond simple ordering. This section explores advanced usage scenarios of Comparable
in Java, including custom sorting logic, handling null values, and integrating with sorted collections.
5.1. Custom Sorting Logic
The compareTo()
method allows for complex custom sorting logic, enabling you to define sophisticated ordering criteria based on multiple fields or dynamic conditions.
5.1.1. Example: Sorting a List of Contacts by Last Name, First Name
Consider a Contact
class with attributes like firstName
and lastName
. You can implement Comparable<Contact>
to define the natural ordering based on last name and then first name.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Contact implements Comparable<Contact> {
private String firstName;
private String lastName;
public Contact(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
@Override
public int compareTo(Contact other) {
int lastNameComparison = this.lastName.compareTo(other.lastName);
if (lastNameComparison != 0) {
return lastNameComparison;
}
return this.firstName.compareTo(other.firstName);
}
@Override
public String toString() {
return "Contact{firstName='" + firstName + "', lastName='" + lastName + '}';
}
}
public class ContactComparableExample {
public static void main(String[] args) {
List<Contact> contacts = new ArrayList<>();
contacts.add(new Contact("Alice", "Smith"));
contacts.add(new Contact("Bob", "Johnson"));
contacts.add(new Contact("Charlie", "Smith"));
Collections.sort(contacts);
for (Contact contact : contacts) {
System.out.println(contact);
}
}
}
In this example, the compareTo()
method first compares contacts based on their last names. If the last names are different, it returns the result of the last name comparison. If the last names are the same, it compares the first names.
Alt: Sorting list of contacts by last name and first name using Comparable interface in Java
5.1.2. Dynamic Sorting Criteria
You can also implement dynamic sorting criteria by using external parameters or flags to determine the sorting order. This allows you to change the sorting logic at runtime without modifying the class itself.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Task implements Comparable<Task> {
private String description;
private int priority;
private boolean isCompleted;
public Task(String description, int priority, boolean isCompleted) {
this.description = description;
this.priority = priority;
this.isCompleted = isCompleted;
}
public String getDescription() {
return description;
}
public int getPriority() {
return priority;
}
public boolean isCompleted() {
return isCompleted;
}
private static boolean sortByPriority = true; // Default sorting by priority
public static void setSortByPriority(boolean sortByPriority) {
Task.sortByPriority = sortByPriority;
}
@Override
public int compareTo(Task other) {
if (sortByPriority) {
int priorityComparison = Integer.compare(this.priority, other.priority);
if (priorityComparison != 0) {
return priorityComparison;
}
}
return this.description.compareTo(other.description);
}
@Override
public String toString() {
return "Task{description='" + description + "', priority=" + priority + "', isCompleted=" + isCompleted + '}';
}
}
public class TaskComparableExample {
public static void main(String[] args) {
List<Task> tasks = new ArrayList<>();
tasks.add(new Task("Implement feature A", 2, false));
tasks.add(new Task("Fix bug B", 1, true));
tasks.add(new Task("Write documentation C", 3, false));
// Sort by priority (default)
Collections.sort(tasks);
System.out.println("Sorted by priority: " + tasks);
// Sort by description
Task.setSortByPriority(false);
Collections.sort(tasks);