Do you need to know how to sort Java objects in a collection, array, or map? Here's how to use the Comparable and Comparator interfaces and avoid ClassCastExceptions. Credit: patpitchaya/Shutterstock Programmers frequently need to sort elements from a database into a collection, array, or map. In Java, we can implement whatever sorting algorithm we want with any type. Using the Comparable interface and compareTo() method, we can sort using alphabetical order, String length, reverse alphabetical order, or numbers. The Comparator interface allows us to do the same but in a more flexible way. Whatever we want to do, we just need to know how to implement the correct sort logic for the given interface and type. Sorting with Java’s Comparable and Comparator Here’s what you’ll learn in this article about sorting Java objects: Sorting with Comparable: Sorting a Java List How Java’s compareTo() works How to sort arrays in Java Sorting a Java Map with TreeMap Sorting a Java Set with TreeSet How to avoid ClassCastExceptions when sorting Using Comparable with the core Java classes Sorting with Comparator: Using Comparator with anonymous inner classes Using Comparator with lambda expressions Sorting with Comparable We’ll start with how to sort using Java’s Comparable interface. We use Comparable when there is a single, default comparison for the object we want sorted. Sorting a Java List In this first example, we implement Comparable in a Simpson class, using Simpson in the generic type: class Simpson implements Comparable<Simpson> { String name; Simpson(String name) { this.name = name; } @Override public int compareTo(Simpson simpson) { return this.name.compareTo(simpson.name); } } public class SimpsonSorting { public static void main(String... sortingWithList) { List<SimpsonCharacter> simpsons = new ArrayList<>(); simpsons.add(new SimpsonCharacter("Homer ")); simpsons.add(new SimpsonCharacter("Marge ")); simpsons.add(new SimpsonCharacter("Bart ")); simpsons.add(new SimpsonCharacter("Lisa ")); Collections.sort(simpsons); simpsons.stream().map(s -> s.name).forEach(System.out::print); Collections.reverse(simpsons); simpsons.stream().forEach(System.out::print); } } Note that we’ve overridden the compareTo() method and passed in another Simpson object. We’ve also overridden the toString() method, just to make the example easier to read. How Java’s compareTo() works The compareTo() method compares a given object or the current instance with a specified object to determine the order of objects. Here’s a quick look at how compareTo() works. We can only use classes that are comparable with the sort() method. If we try to pass a Simpson that does not implement Comparable, we will receive a compilation error. The sort() method uses polymorphism by passing any object that is Comparable. Objects will then be sorted as expected. The output from the previous code would be: Bart Homer Lisa Marge If we wanted to reverse the order, we could exchange the sort() for a reverse(); from: Collections.sort(simpsons); to: Collections.reverse(simpsons); Deploying the reverse() method would change the previous output to: Marge Lisa Homer Bart How to sort a Java array In Java, we can sort an array with any type we want as long as it implements the Comparable interface. Here’s an example: public class ArraySorting { public static void main(String... moeTavern) { int[] moesPints = new int[] {9, 8, 7, 6, 1}; Arrays.sort(moesPints); Arrays.stream(moesPints).forEach(System.out::print); Simpson[] simpsons = new Simpson[]{new Simpson("Lisa"), new Simpson("Homer")}; Arrays.sort(simpsons); Arrays.stream(simpsons).forEach(System.out::println); } } In the first sort() invocation, the array is sorted to: 1 6 7 8 9 In the second sort() invocation, it is sorted to: Homer Lisa Keep in mind that custom objects must implement Comparable to be sorted, even as an array. Avoiding ClassCastExceptions when sorting Java objects If the Simpson object wasn’t implementing Comparable, a ClassCastException would be thrown. If you run this as a test, you will see something like the following output: Error:(16, 20) java: no suitable method found for sort(java.util.List<com.javaworld.javachallengers.sortingcomparable.Simpson>) method java.util.Collections.<T>sort(java.util.List<T>) is not applicable (inference variable T has incompatible bounds equality constraints: com.javaworld.javachallengers.sortingcomparable.Simpson lower bounds: java.lang.Comparable<? super T>) method java.util.Collections.<T>sort(java.util.List<T>,java.util.Comparator<? super T>) is not applicable (cannot infer type-variable(s) T (actual and formal argument lists differ in length)) This log may be confusing but don’t worry. Just keep in mind that a ClassCastException will be thrown for any sorted object that doesn’t implement the Comparable interface. Sorting a Map with TreeMap The Java API includes many classes to assist with sorting, including TreeMap. In the example below, we use TreeMap to sort keys into a Map. public class TreeMapExample { public static void main(String... barney) { Map<SimpsonCharacter, String> simpsonsCharacters = new TreeMap<>(); simpsonsCharacters.put(new SimpsonCharacter("Moe"), "shotgun"); simpsonsCharacters.put(new SimpsonCharacter("Lenny"), "Carl"); simpsonsCharacters.put(new SimpsonCharacter("Homer"), "television"); simpsonsCharacters.put(new SimpsonCharacter("Barney"), "beer"); System.out.println(simpsonsCharacters); } } TreeMap uses the compareTo() method implemented by the Comparable interface. Each element in the resulting Map is sorted by its key. In this case, the output would be: Barney=beer, Homer=television, Lenny=Carl, Moe=shotgun Remember, though: if the object doesn’t implement Comparable, you will get a ClassCastException. Sorting a Set with TreeSet The Set interface is responsible for storing unique values, but when we use the TreeSet implementation, inserted elements will be automatically sorted as we add them: public class TreeSetExample { public static void main(String... barney) { Set<SimpsonCharacter> simpsonsCharacters = new TreeSet<>(); simpsonsCharacters.add(new SimpsonCharacter("Moe")); simpsonsCharacters.add(new SimpsonCharacter("Lenny")); simpsonsCharacters.add(new SimpsonCharacter("Homer")); simpsonsCharacters.add(new SimpsonCharacter("Barney")); System.out.println(simpsonsCharacters); } } The output from this code is: Barney, Homer, Lenny, Moe Again, if we use an object that is not Comparable, we’ll get a ClassCastException. Up next: Using Comparable with the core Java classes Using Comparable with the core Java classes Many core Java classes and objects implement the Comparable interface, which means we don’t have to implement the compareTo() logic for those classes. Here are a few familiar examples: String public final class String implements java.io.Serializable, Comparable<String>, CharSequence { ... Integer public final class Integer extends Number implements Comparable<Integer> { … Double public final class Double extends Number implements Comparable<Double> {... Sorting with Comparator What if we didn’t want to use the same compareTo() method from the POJO class? Could we override the Comparable method to use a different logic? Below is an example: public class BadExampleOfComparable { public static void main(String... args) { List<SimpsonCharacter> characters = new ArrayList<>(); SimpsonCharacter homer = new SimpsonCharacter("Homer") { @Override public int compareTo(SimpsonCharacter simpson) { return this.name.length() - (simpson.name.length()); } }; SimpsonCharacter moe = new SimpsonCharacter("Moe") { @Override public int compareTo(SimpsonCharacter simpson) { return this.name.length() - (simpson.name.length()); } }; characters.add(homer); characters.add(moe); Collections.sort(characters); System.out.println(characters); } } As you can see, this code is complicated and includes a lot of repetition. We had to override the compareTo() method twice for the same logic. If there were more elements we would have to replicate the logic for each object. Fortunately, we have the Comparator interface, which lets us detach the compareTo() logic from Java classes. Consider the same example above rewritten using Comparator: public class GoodExampleOfComparator { public static void main(String... args) { List<SimpsonCharacter> characters = new ArrayList<>(); SimpsonCharacter homer = new SimpsonCharacter("Homer"); SimpsonCharacter moe = new SimpsonCharacter("Moe"); characters.add(homer); characters.add(moe); Collections.sort(characters, (Comparator.<SimpsonCharacter> comparingInt(character1 -> character1.name.length()) .thenComparingInt(character2 -> character2.name.length()))); System.out.println(characters); } } Using Comparator with anonymous inner classes In this next example, we use an anonymous inner class to compare the value of objects. An anonymous inner class, in this case, is any class that implements Comparator. Using it means we are not bound to instantiating a named class implementing an interface; instead, we implement the compareTo() method inside the anonymous inner class. public class MarvelComparator { public static void main(String... comparator) { List<String> marvelHeroes = new ArrayList<>(); marvelHeroes.add("SpiderMan "); marvelHeroes.add("Wolverine "); marvelHeroes.add("Xavier "); marvelHeroes.add("Cyclops "); Collections.sort(marvelHeroes, new Comparator<String>() { @Override public int compare(String hero1, String hero2) { return hero1.compareTo(hero2); } }); Collections.sort(marvelHeroes, (m1, m2) -> m1.compareTo(m2)); Collections.sort(marvelHeroes, Comparator.naturalOrder()); marvelHeroes.forEach(System.out::print); } } Using Comparator with lambda expressions Anonymous inner classes are verbose, which can cause problems in our code. In the Comparator interface, we can use lambda expressions to simplify and make the code easier to read. For example, we could change this: Collections.sort(marvel, new Comparator<String>() { @Override public int compare(String hero1, String hero2) { return hero1.compareTo(hero2); } }); to this: Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2)); Less code and the same result! The output of this code would be: Cyclops SpiderMan Wolverine Xavier We could make the code even simpler by changing this: Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2)); to this: Collections.sort(marvel, Comparator.naturalOrder()); Take the Comparable interface challenge! Test what you’ve learned by figuring out the output of the following code. Remember, you’ll learn best if you solve this challenge for yourself just by studying it. Once you’ve reached an answer, you can check the answer below. You can also run your own tests to fully absorb the concepts. public class SortComparableChallenge { public static void main(String... doYourBest) { Set<Simpson> set = new TreeSet<>(); set.add(new Simpson("Homer")); set.add(new Simpson("Marge")); set.add(new Simpson("Lisa")); set.add(new Simpson("Bart")); set.add(new Simpson("Maggie")); List<Simpson> list = new ArrayList<>(); list.addAll(set); Collections.reverse(list); list.forEach(System.out::println); } static class Simpson implements Comparable<Simpson> { String name; public Simpson(String name) { this.name = name; } public int compareTo(Simpson simpson) { return simpson.name.compareTo(this.name); } public String toString() { return this.name; } } } What is the output of this code? A) Bart Homer Lisa Maggie Marge B) Maggie Bart Lisa Marge Homer C) Marge Maggie Lisa Homer Bart D) Indeterminate TreeSet and reverse() Looking at the code, the first thing you should notice is that we’re using a TreeSet , so the elements will be sorted automatically. The second thing is that the order of comparison is inverted, so the sorting will be done in reverse order. When we first add elements into the list, TreeSet automatically sorts them to: Marge Maggie Lisa Homer Bart Then we use the reverse() method, which reverses the order of elements. So the final output would be A): Bart Homer Lisa Maggie Marge Video challenge! Debugging sort() and TreeSet Debugging is one of the easiest ways to fully absorb programming concepts while also improving your code. In this video you can follow along while I debug and explain the Java sorting challenge: Common mistakes with Comparable Trying to sort a non-comparable object in the sort() method. Using Comparable for different sorting strategies within the same object. Inverting comparison in the compareTo() method so that sorting will be in reverse order, like so: Normal order: public int compareTo(Simpson simpson) { this.name.compareTo(simpson.name); } Inverted order: public int compareTo(Simpson simpson) { simpson.name.compareTo(this.name); } Adding a non-comparable (any object that doesn’t implement Comparable) object in a TreeSet or TreeMap object. What to remember about sorting with Java Use Comparable when the comparison is standard for the given class. Use Comparator when you need more flexibility. It’s possible to use lambdas with Comparator. Many of Java’s core classes implement Comparable. Use TreeMap or TreeSet when sorting a Map or a Set. The compareTo() method works with both Comparable and Comparator. The compareTo() method will return a positive number if one object is greater than the other, negative if it’s lower, and zero if they are the same. Related content feature 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos