Question

How to Sort an ArrayList of Custom Objects by Date in Java

javasortingdatecomparator

Question

I want to sort an ArrayList of custom Java objects by one of their properties, which is a Date returned by getStartDate().

I have read about sorting ArrayList objects using a Comparator, but many examples use compareTo(), which confused me because I thought that method was mainly associated with types like String.

Usually, I compare two dates like this:

item1.getStartDate().before(item2.getStartDate())

So I was wondering whether I could create a comparator like this:

public class CustomComparator {
    public boolean compare(Object object1, Object object2) {
        return object1.getStartDate().before(object2.getStartDate());
    }
}

public class RandomName {
    // ...
    Collections.sort(Database.arrayList, new CustomComparator());
    // ...
}

What is the correct way to sort an ArrayList of custom objects by a Date field in Java?

Short Answer

By the end of this page, you will understand how sorting works in Java for custom objects, why Comparator uses an int-returning compare() method instead of a boolean, and how to sort an ArrayList by a Date property safely and clearly.

Concept

In Java, sorting works by asking one question repeatedly: which of these two items should come first?

For custom objects, Java cannot guess the correct order unless you tell it how to compare them. That is where a Comparator comes in.

A Comparator<T> is an object that defines sorting rules for type T. Its compare(T a, T b) method must return:

  • a negative number if a should come before b
  • 0 if they are equal for sorting purposes
  • a positive number if a should come after b

This is why returning boolean is incorrect. Sorting needs more than just true/false. It needs to know whether one item is less than, equal to, or greater than another.

compareTo() is related, but it is not only for String. Many Java classes implement Comparable, including String, Integer, and Date. For example, Date already has a compareTo() method.

That means when comparing two Date values, you can write:

item1.getStartDate().compareTo(item2.getStartDate())

This works well inside a comparator.

Why this matters in real programming:

  • sorting events by date
  • ordering messages by timestamp
  • showing tasks by deadline
  • listing logs from oldest to newest
  • presenting records in user-friendly order

Without a correct comparator, sorting custom objects will either fail to compile or produce incorrect results.

Mental Model

Think of sorting like arranging index cards on a desk.

Each card is a custom object. Java picks up two cards at a time and asks your comparator:

  • Should card A go before card B?
  • Should card B go before card A?
  • Or are they equal in order?

A boolean answer is too limited because it only gives part of the story. Java needs a full ranking answer:

  • negative = A first
  • zero = same position
  • positive = B first

So a comparator is like a sorting rulebook that explains how to order your objects based on one property, such as a start date.

Take Quiz

Syntax and Examples

Basic comparator syntax

import java.util.Comparator;

public class CustomComparator implements Comparator<MyItem> {
    @Override
    public int compare(MyItem item1, MyItem item2) {
        return item1.getStartDate().compareTo(item2.getStartDate());
    }
}

Then use it like this:

Collections.sort(myList, new CustomComparator());

Complete example

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;

class Event {
    private String name;
    private Date startDate;

    public Event(String name, Date startDate) {
        this.name = name;
        this.startDate = startDate;
    }

    public String getName() {
        return name;
    }

    public Date getStartDate() {
        return startDate;
    }

    @Override
    public String toString() {
        return name + " - " + startDate;
    }
}

class EventDateComparator implements Comparator<Event> {
    @Override
    public int compare(Event e1, Event e2) {
        return e1.getStartDate().compareTo(e2.getStartDate());
    }
}

public class Main {
    public static void main(String[] args) {
        ArrayList<Event> events = new ArrayList<>();

        events.add(new Event("Meeting", new Date(124, 5, 10)));
        events.add(new Event("Workshop", new Date(124, 4, 20)));
        events.add(new Event("Launch", new Date(124, 6, 1)));

        Collections.sort(events, new EventDateComparator());

        for (Event event : events) {
            System.out.println(event);
        }
    }
}

Simpler modern style

If you are using Java 8+, you can write:

events.sort(Comparator.comparing(Event::getStartDate));

This is shorter and commonly used in modern Java code.

Descending order

events.sort(Comparator.comparing(Event::getStartDate).reversed());

This sorts from newest to oldest.

Step by Step Execution

Consider this code:

ArrayList<Event> events = new ArrayList<>();
events.add(new Event("A", new Date(124, 4, 20)));
events.add(new Event("B", new Date(124, 4, 18)));
events.add(new Event("C", new Date(124, 4, 25)));

events.sort(Comparator.comparing(Event::getStartDate));

What happens step by step

  1. Java stores three Event objects in the list.
  2. events.sort(...) starts the sorting process.
  3. Java compares pairs of Event objects.
  4. For each comparison, it calls getStartDate() on both objects.
  5. The comparator compares the two Date objects.
  6. If the first date is earlier, it returns a negative value.
  7. If the dates are the same, it returns 0.
  8. If the first date is later, it returns a positive value.
  9. Java rearranges the list based on those results.
  10. The final order becomes:
B
A
C

because May 18 comes before May 20, and May 20 comes before May 25.

Small trace

Suppose Java compares A and B:

A.getStartDate().compareTo(B.getStartDate())
  • A = May 20
  • B = May 18
  • result is positive
  • so B should come before A

Then Java compares A and C:

  • A = May 20
  • C = May 25
  • result is negative
  • so A should stay before C

Real World Use Cases

Sorting custom objects by a property is extremely common.

Examples

  • Calendar apps: sort events by start date
  • Task managers: sort tasks by due date
  • E-commerce systems: sort orders by creation time
  • Messaging apps: sort conversations by latest message date
  • Logging tools: sort log entries by timestamp
  • Booking systems: sort reservations by check-in date

Why comparators are useful

A single object can be sorted in different ways depending on the feature:

  • by date
  • by name
  • by priority
  • by price

Using a Comparator lets you define different sorting rules without changing the class itself.

Take Quiz

Real Codebase Usage

In real projects, developers often use comparators in a few common patterns.

1. Sorting by one field

users.sort(Comparator.comparing(User::getCreatedAt));

2. Sorting by multiple fields

If two dates are the same, sort by name:

events.sort(
    Comparator.comparing(Event::getStartDate)
              .thenComparing(Event::getName)
);

3. Handling null values safely

If some dates may be null:

events.sort(
    Comparator.comparing(Event::getStartDate, Comparator.nullsLast(Date::compareTo))
);

4. Early validation before sorting

Developers often validate data before sorting:

if (events == null || events.isEmpty()) {
    return;
}

events.sort(Comparator.comparing(Event::getStartDate));

5. Keeping comparison logic reusable

A comparator may be stored as a constant:

public static final Comparator<Event> BY_START_DATE =
    Comparator.comparing(Event::getStartDate);

This makes sorting rules easier to reuse across services, controllers, and tests.

Common Mistakes

1. Returning boolean from compare()

This is incorrect:

public boolean compare(Object object1, Object object2) {
    return object1.getStartDate().before(object2.getStartDate());
}

Why it is wrong:

  • Comparator.compare() must return int
  • sorting needs three outcomes, not just true/false

2. Not implementing Comparator<T>

Broken example:

public class CustomComparator {
    public int compare(MyItem a, MyItem b) {
        return a.getStartDate().compareTo(b.getStartDate());
    }
}

This defines a method, but the class does not actually implement Comparator<MyItem>.

Correct version:

public class CustomComparator implements Comparator<MyItem> {
    @Override
    public int compare(MyItem a, MyItem b) {
        return a.getStartDate().compareTo(b.getStartDate());
    }
}

3. Using raw Object types instead of generics

Broken style:

public int compare(Object a, Object b)

Problems:

  • loses type safety
  • requires casting
  • makes code harder to read

Better:

public int compare(Event a, Event b)

4. Forgetting parentheses when creating the comparator

Broken:

Collections.sort(events, new CustomComparator);

Correct:

Collections.sort(events, new CustomComparator());

5. Ignoring null dates

If getStartDate() can return null, this may fail:

return a.getStartDate().compareTo(b.getStartDate());

Safer version:

return Comparator.nullsLast(Date::compareTo)
       .compare(a.getStartDate(), b.getStartDate());

6. Confusing Comparator with Comparable

  • Comparable = the class defines its own default ordering
  • Comparator = external sorting rule

If you only need one natural order for the class, Comparable may make sense. If you need multiple sorting rules, use Comparator.

Comparisons

Comparator vs Comparable

FeatureComparatorComparable
Where sorting logic livesOutside the classInside the class
Methodcompare(a, b)compareTo(other)
Return typeintint
Good for multiple sort ordersYesNo
Good for natural/default orderSometimesYes

before() vs compareTo() for Date

ApproachReturnsBest use
date1.before(date2)booleanSimple yes/no check
date1.compareTo(date2)intSorting and ordering

Old style vs modern style

StyleExampleNotes
Separate comparator classnew EventDateComparator()Clear and reusable
Anonymous classnew Comparator<Event>() { ... }Older Java style
Lambda / method referenceComparator.comparing(Event::getStartDate)Short and common in Java 8+

Collections.sort() vs List.sort()

MethodExampleNotes
Collections.sort(list, comparator)Classic approachWorks well and is widely seen
list.sort(comparator)Modern approachShorter and preferred in newer code
Take Quiz

Cheat Sheet

Quick rules

  • Use Comparator<T> to sort custom objects.
  • compare(T a, T b) must return an int.
  • Return:
    • negative if a comes first
    • 0 if equal
    • positive if b comes first
  • Date already implements Comparable<Date>.
  • For dates, use date1.compareTo(date2) inside the comparator.

Common syntax

Comparator class

class EventDateComparator implements Comparator<Event> {
    @Override
    public int compare(Event e1, Event e2) {
        return e1.getStartDate().compareTo(e2.getStartDate());
    }
}

Sort with Collections.sort

Collections.sort(events, new EventDateComparator());

Sort with List.sort

events.sort(Comparator.comparing(Event::getStartDate));

Descending order

events.sort(Comparator.comparing(Event::getStartDate).reversed());

Handle null dates

events.sort(
    Comparator.comparing(Event::getStartDate, Comparator.nullsLast(Date::compareTo))
);

Remember

  • before() is for true/false checks
  • compareTo() is for sorting
  • prefer generics over Object
  • include () when creating a new comparator object

FAQ

Can I sort custom objects in Java without changing the class?

Yes. Use a Comparator when you want to define sorting outside the class.

Why does Comparator.compare() return int instead of boolean?

Because sorting needs to know whether one item is less than, equal to, or greater than another.

Is compareTo() only for String?

No. Many Java classes implement Comparable, including String, Integer, and Date.

How do I sort by a Date field in Java?

Use a comparator that compares the date values, such as:

Comparator.comparing(MyClass::getStartDate)

What if two objects have the same date?

The comparator returns 0 for that comparison. You can add a second rule with thenComparing() if needed.

Should I use Collections.sort() or list.sort()?

Both work. list.sort() is the more modern and shorter style.

What happens if a date is null?

A normal comparison may throw a NullPointerException. Use Comparator.nullsFirst() or Comparator.nullsLast().

When should I use Comparable instead of Comparator?

Use Comparable when the class has one natural default order. Use Comparator when you want flexible or multiple sorting rules.

Take Quiz

Mini Project

Description

Create a small Java program that manages a list of events and sorts them by their start date. This project demonstrates how to define custom objects, store them in an ArrayList, and order them using a comparator.

Goal

Build a program that prints events in chronological order based on a Date property.

Requirements

  • Create an Event class with name and startDate fields.
  • Add at least four Event objects to an ArrayList.
  • Sort the list by startDate from earliest to latest.
  • Print the sorted events to the console.
Take Quiz

Keep learning

Related questions

View more