Other Answers are correct. But here is some alternate syntax, and use of correct date-time type. And we see the new SequencedCollection features in Java 21+.
tl;dr
Define your class as a record that implements Comparable.
record CarSale( String name , Instant whenSold ) implements Comparable < CarSale >
{
private static final Comparator < CarSale > COMPARATOR =
Comparator
.comparing ( CarSale :: whenSold )
.thenComparing ( CarSale :: name );
@Override
public int compareTo ( final CarSale other )
{
return CarSale.COMPARATOR.compare ( this , other );
}
}
Define your resulting sorted collection as a SequencedCollection whose implementation is a TreeSet. Get the latest car sale by getting the last element of the collection.
CarSale latest =
new TreeSet <> (
List
.of (
new CarSale ( "BMW" , Instant.parse ( "2023-01-01T02:00:00.800Z" ) ) ,
new CarSale ( "Benz" , Instant.parse ( "2023-01-03T02:00:00.800Z" ) ) ,
new CarSale ( "Jeep" , Instant.parse ( "2023-01-02T02:00:00.800Z" ) )
)
)
.getLast ( ); // Java 21+.
CarSale[name=Benz, whenSold=2023-01-03T02:00:00.800Z]
Details
Naming
Your names are a bit vague. The sold could be more clear such as whenSold. And Car class is really about when a car was sold. So CarSale would be more precise.
Record
If the main purpose of your Car class is to transparently communicate shallowly-immutable data, define your class as a record. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.
record CarSale ( String name , String whenSold ) {}
java.time.Instant
That second parameter, whenSold, represents a moment, a point on the timeline. Java includes the java.time classes for such date-time work. Use java.time.Instant to represent a point on the timeline as seen in UTC (an offset from UTC of zero hours-minutes-seconds).
record CarSale ( String name , Instant whenSold ) {}
Your input strings use standard ISO 8601 format to represent that moment. The java.time classes by default use ISO 8601 formats when parsing/generating text. So we can directly parse your inputs.
CarSale car = new CarSale ( "BMW" , Instant.parse( "2023-01-01T02:00:00.800Z" ) ) ;
List.of
You can collect your instantiated CarSale objects more compactly with List.of. This produces an unmodifiable List object.
Your example data is already in the desired sort order. So I changed your original order so we can verify sorting is happening.
List < CarSale > carSales =
List.of (
new CarSale ( "BMW" , Instant.parse ( "2023-01-01T02:00:00.800Z" ) ) ,
new CarSale ( "Benz" , Instant.parse ( "2023-01-03T02:00:00.800Z" ) ),
new CarSale ( "Jeep" , Instant.parse ( "2023-01-02T02:00:00.800Z" ) )
);
Comparable
Other Answers apply a Comparator. That is a valid approach. But if your primary default way to sort these CarSale records is by whenSold, then build that into your class as a feature. To build that in, implement the Comparable interface with its compareTo method.
To implement compareTo, we can define a contained Comparator to do the work. Make it static to have a singleton constant.
When sorting on whenSold, we could have a tie when two or more cars are sold at the same moment. To break the tie, we can look at the only other property, name.
private static final Comparator < CarSale > COMPARATOR =
Comparator
.comparing ( CarSale :: whenSold )
.thenComparing ( CarSale :: name );
Finish the compareTo method.
@Override
public int compareTo ( final CarSale other )
{
return CarSale.COMPARATOR.compare ( this , other );
}
Sorting
Finally, we get to the sorting.
We have an unmodifiable list, which cannot be sorted. So we need to create a modifiable collection that can be sorted. We can do that by passing the unmodifiable list to the constructor of a modifiable List implementation.
new ArrayList <> ( carSales )
Stream sorting
From there we can sort by making a stream, asking the stream to sort itself using the "natural order" of our CarSale objects. The natural order is the order determined by its being Comparable. We collect the resulting sorted elements into another List.
List < CarSale > carSalesSorted = new ArrayList <> ( carSales ).stream ( ).sorted ( ).toList ( );
SequencedCollection
In Java 21, we can use the more general interface SequencedCollection rather than List. See JEP 431: Sequenced Collections.
SequencedCollection < CarSale > carSalesSorted = new ArrayList <> ( carSales ).stream ( ).sorted ( ).toList ( );
TreeSet in place of ArrayList
At this point, we realize that we can use a different implementation of SequencedCollection. We can use TreeSet which automatically keeps its elements in sorted order. This works only if we do not care about tracking duplicate objects, as Set implementations like TreeSet forbid duplicates. We can simply pass our unmodifiable List to the constructor of TreeSet.
SequencedCollection < CarSale > carSalesSorted = new TreeSet <> ( carSales );
To access the elements, iterate. For example, call java.lang.Iterable#forEach. This works because every SequencedCollection object is also an Iterable object.
carSalesSorted.forEach ( System.out::println );
When run:
CarSale[name=BMW, whenSold=2023-01-01T02:00:00.800Z]
CarSale[name=Jeep, whenSold=2023-01-02T02:00:00.800Z]
CarSale[name=Benz, whenSold=2023-01-03T02:00:00.800Z]
getLast
Determine the latest car sale by getting the last element from our SequencedCollection. Simply call getLast.
CarSale latest = carSalesSorted.getLast ( );
CarSale[name=Benz, whenSold=2023-01-03T02:00:00.800Z]
Complete code example:
package work.basil.example;
import java.time.Instant;
import java.util.*;
public class CarsLedger
{
public static void main ( String[] args )
{
record CarSale( String name ,
Instant whenSold ) implements Comparable < CarSale >
{
private static final Comparator < CarSale > COMPARATOR =
Comparator
.comparing ( CarSale :: whenSold )
.thenComparing ( CarSale :: name );
@Override
public int compareTo ( final CarSale other )
{
return CarSale.COMPARATOR.compare ( this , other );
}
}
List < CarSale > carSales =
List.of (
new CarSale ( "BMW" , Instant.parse ( "2023-01-01T02:00:00.800Z" ) ) ,
new CarSale ( "Benz" , Instant.parse ( "2023-01-03T02:00:00.800Z" ) ) ,
new CarSale ( "Jeep" , Instant.parse ( "2023-01-02T02:00:00.800Z" ) )
);
//SequencedCollection < CarSale > carSalesSorted = new ArrayList <> ( carSales ).stream ( ).sorted ( ).toList ( );
SequencedCollection < CarSale > carSalesSorted = new TreeSet <> (
List.of (
new CarSale ( "BMW" , Instant.parse ( "2023-01-01T02:00:00.800Z" ) ) ,
new CarSale ( "Benz" , Instant.parse ( "2023-01-03T02:00:00.800Z" ) ) ,
new CarSale ( "Jeep" , Instant.parse ( "2023-01-02T02:00:00.800Z" ) )
)
);
carSalesSorted.forEach ( System.out :: println );
CarSale latest = carSalesSorted.getLast ( );
System.out.println ( "latest = " + latest );
}
}
SequencedCollectionanswer below - about as up-to-date as it gets. As others have said, you should use a properjava.timetype for your sold date, but since you use proper ISO formats, you can sort as strings withCar latestSold = carsList.stream().max(Comparator.comparing(Car::getSold)).orElseThrow();soldis anInstantor other date-time object as in a couple of the answers:Collcetions.max(carList, Comparator.comparing(Car::getSold))gives you the most recently sold car.