The collections framework interfaces and classes are among the widely used built-in utilities in the Java ecosystem. An ArrayList is a generic class in the collections framework. It is the most used of all the Java collections classes. As a result, it is one of the must know Java classes.
This blog will discuss storing objects in an ArrayList in a sorted order. Out of the box, an ArrayList stores objects in the order of insertion. This means that the first inserted object goes at first index, while the last inserted object goes at the last index. You can change this behaviour by using the overloaded add(int i, Object o) method.
But in some cases, you will need to sort your ArrayList to meet project requirements. So, lets look at the various ways of sorting your List.
1. Using A TreeSet
A TreeSet is a set that has the property of storing objects in ordered way. You can sort your items by copying them into a TreeSet.
List<Integer> integers = new ArrayList<>();
integers.add(3);
integers.add(5);
integers.add(2);
integers.add(4);
integers.add(1);
System.out.println(integers); // output: [3, 5, 2, 4, 1]
Set<Integer> integerSet = new TreeSet<>();
integerSet.addAll(integers);
System.out.println(integerSet); // output: [1, 2, 3, 4, 5]
But there are two reasons why you should not do this. First, you will lose all the duplicate records. Second, creating another object would be a waste of system resources.
List<Integer> integers = new ArrayList<>();
integers.add(3);
integers.add(5);
integers.add(2);
integers.add(4);
integers.add(4);
integers.add(3);
integers.add(1);
System.out.println(integers); // output: [3, 5, 2, 4, 4, 3, 1]
Set<Integer> integerSet = new TreeSet<>();
integerSet.addAll(integers);
System.out.println(integerSet); // output: [1, 2, 3, 4, 5]. Two items are lost as Sets don't do duplicates
2. Static sort method in Collections class
The Collections class has a sort() method that can you can use to sort a List. By itself, it sorts a List based on its natural order. The sort() method is overloaded, though. The with two parameters (List and Comparator), it's possible to specify the order. The order can be either the natural or the reverse order.
List<Integer> integers = new ArrayList<>();
integers.add(3);
integers.add(5);
integers.add(2);
integers.add(4);
integers.add(4);
integers.add(3);
integers.add(1);
System.out.println(integers); // output: [3, 5, 2, 4, 4, 3, 1]
Collections.sort(integers);
System.out.println(integers); // output: [1, 2, 3, 3, 4, 4, 5]
List<Integer> integers = new ArrayList<>();
integers.add(3);
integers.add(5);
integers.add(2);
integers.add(4);
integers.add(4);
integers.add(3);
integers.add(1);
System.out.println(integers); // output: [3, 5, 2, 4, 4, 3, 1]
Collections.sort(integers, Comparator.reverseOrder());
System.out.println(integers); // output: [5, 4, 4, 3, 3, 2, 1]
This method mutates the original list. For the most part, this works for most applications. In rare cases though, this may not be the intended behaviour. Always be careful if there are methods that consume the unsorted version of the list.
3. ArrayList's built-in sort method
This method functions like the Collections.sort() method. For code readability, you should go with this method over the Collections.sort() method. This method takes one argument of the Comparator type.
List<Integer> integers = new ArrayList<>();
integers.add(3);
integers.add(5);
integers.add(2);
integers.add(4);
integers.add(4);
integers.add(3);
integers.add(1);
System.out.println(integers); // output: [3, 5, 2, 4, 4, 3, 1]
integers.sort(null); // functions same as integers.sort(Comparator.naturalOrder());
System.out.println(integers); // output: [1, 2, 3, 3, 4, 4, 5]
List<Integer> integers = new ArrayList<>();
integers.add(3);
integers.add(5);
integers.add(2);
integers.add(4);
integers.add(4);
integers.add(3);
integers.add(1);
System.out.println(integers); // output: [3, 5, 2, 4, 4, 3, 1]
integers.sort(Comparator.reverseOrder());
System.out.println(integers); // output: [5, 4, 4, 3, 3, 2, 1]
4. Overriding the add method
All of the techniques described above involved sorting fully populated ArrayLists. There is an assumption that the List will not receive more data after that. This is hard to guarantee in real world applications. It is likely that different methods will be populating data in the List.
In most cases, it is not ideal to call the sort() method after each data addition, for performance reasons. It is also possible that a method reads data from the ArrayList before the sort method is executed. Unexpected behaviours and inconsistencies could arise from this. To mitigate it, it is important that you, the developer, have the assurance that the ArrayList is always sorted.
To do this we can override the add() and addAll() methods. Then, we can sort the data during insertion.
After overriding the add method, we can insert an object at a specified index. We perform a binary search on the data to find the index.
In the addAll() method, we can make sure data is always sorted by calling the sort() method after a call to the super.addAll() method.
List<Integer> integers = new ArrayList<>(){
@Override
public boolean add(Integer value) {
int index = Collections.binarySearch(this, value); // using binary search to determine the index
if (index < 0) index = ~index;
super.add(index, value);
return true;
};
@Override
public boolean addAll(Collection<? extends Integer> c) {
boolean added = super.addAll(c);
if (added) this.sort(null);
return added;
}
};
integers.add(3);
integers.add(5);
integers.add(2);
integers.add(4);
integers.add(4);
integers.add(3);
integers.add(1);
integers.addAll(List.of(8, 9, 6, 7, 10));
System.out.println(integers); // output: [1, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10]
Footnote: Thank you for your time. I hope this blog helped you. I wish you all the best. Feel free to follow me on social media (links in the footer).