user2969720
user2969720

Reputation: 115

Best list implementation for ordering objects by field in Java?

The situation I have is that I currently have a LinkedList of 'Appointment' objects which have the following attributes:

'patient' 'date' 'type'

The only functionality required is to loop through this LinkedList and display each appointment in order of 'date'. So, each time a new Appointment is created (using a method called 'addAppointment' in the same class as the LinkedList of appointments), the method will sort the linked list using a Comparator.

However, I was wondering if this is bad practice and if there is a better way to do this? I never need to get the 'Appointment' object by its index and so would it be better practice to use a Priority Queue?

Upvotes: 3

Views: 441

Answers (4)

Basil Bourque
Basil Bourque

Reputation: 338446

NavigableSet

The NavigableSet, and its predecessor, SortedSet, define a contract for a distinct (no duplicates allowed) collection of objects maintained in a certain order.

The TreeSet class bundled with Java implements this interface. For concurrency, or for very large collections, use ConcurrentSkipListSet.

You said:

'Appointment' objects which have the following attributes: 'patient' 'date' 'type'

record Appointment ( String patient , LocalDate date , String type ) {}

Define a Comparator to control the sorting. We pass a method reference to access the member field by which we want to sort. In our example that is the LocalDate field named date.

Comparator < Appointment > comparator = Comparator.comparing( Appointment :: date );

Track our appointments. Specify the comparator to use automatically as elements added to this set.

NavigableSet < Appointment > appointments = new TreeSet <>( comparator );

Sample data.

appointments.addAll(
        List.of(
                new Appointment( "Alice" , LocalDate.of( 2025 , Month.APRIL , 23 ) , "trim" ) ,
                new Appointment( "Bob" , LocalDate.of( 2025 , Month.JANUARY , 23 ) , "dye" ) ,
                new Appointment( "Carol" , LocalDate.of( 2025 , Month.MARCH , 23 ) , "set" )
        )
);

Add another element.

appointments.add(
        new Appointment( "Davis" , LocalDate.of( 2025 , Month.FEBRUARY , 23 ) , "cut" )
);

See results.

System.out.println( "appointments = " + appointments );

appointments = [Appointment[patient=Bob, date=2025-01-23, type=dye], Appointment[patient=Davis, date=2025-02-23, type=cut], Appointment[patient=Carol, date=2025-03-23, type=set], Appointment[patient=Alice, date=2025-04-23, type=trim]]

Sure enough, the Appointment objects are kept in sorted order, arranged by the date.

You might want a secondary sort, to order any multiple appointments for the same date.

Comparator < Appointment > comparator =
        Comparator
                .comparing( Appointment :: date )
                .thenComparing( Appointment :: patient );

Sequenced collections

In Java 21+, we have sequenced collections, adding more interfaces to the Java Collections Framework. These interfaces include:

NavigableSet, SortedSet, TreeSet, and ConcurrentSkipListSet all extend/implement those two new interfaces.

SequencedCollection < Appointment > appointments = new TreeSet <>( comparator );

class hierarchy diagram by Stuart Marks of Oracle Corp.

Upvotes: 1

Scott Shipp
Scott Shipp

Reputation: 2301

I would disagree with everyone here so far and say go ahead and use a simple ArrayList. Your main use case is just to loop through the list and display it in order, right? You can sort an array list by using the Collections.sort() method, which will sort any list of objects implementing Comparable. So just implement Comparable in your Appointment object (only need to provide a .compareTo() method...the API explains this well, or the Java Trail on Collections) and your sorting will then be painless and easy. My experience is this is better performing than an object like TreeSet or a LinkedList.

Now those would be better performing if you were always inserting into the middle of the list a lot, but you seem to mostly be reading this list from what you said, so go for the ArrayList.

Upvotes: 1

Brian
Brian

Reputation: 7326

Use a PriorityQueue

Write a compareTo method for your Appoitnment object that specifies the natural ordering according to date.

PriorityQueue<Appointment> schedule = new PriorityQueue<Appoitnment>();
Appointment a1 = new Appointment();
Appointment a2 = new Appointment();
schedule.add(a1);
schedule.add(a2);

// Iteratively removes the earliest appointment remaining in the schedule
while (schedule.peek != null) {
  System.out.println(schedule.poll().toString());
}

Upvotes: -1

Generally speaking, use an ArrayList when you need random access and a LinkedList when there will be lots of inserts and removes. If you need a data structure that stays sorted, PriorityQueue is an option, but it's really intended for the scenario where you're just pulling the top element off sequentially. If you're needing to repeatedly iterate over the elements, use a SortedSet.

If a field involved in the element's ordering changes after it's inserted into any sorted collection, you should remove and re-add the element (preferably removing before modification).

Upvotes: 0

Related Questions