Sorting CollectionsJ8 Home « Sorting Collections
In our final look at collection we look at sorting our collections using the Comparable
and Comparator
interfaces. We have already seen examples of sorted collections when we looked at
the TreeSet and TreeMap classes earlier in the section. We used some simple code examples to show how these collection types worked and were sorted
in natural order according to their types. Maybe what wasn't apparent at the time was that their was no problem with ordering these collections as we were using String
and Integer
objects implement the Comparable
interface and its only method compareTo()
. We also used the Collections.sort()
method in the last lesson to sort
a list, this also used a String
object.
But what if we create a TreeSet or TreeMap collection or try to use the Collections.sort()
method to sort a custom class? To answer this question we will use the Contractor
class we created in the Overriding equals()
section of the The Object
Superclass lesson. The Contractor
class was tested to ensure that the overrides of
equals()
and hashCode()
worked correctly. When creating your own collections you will
always want to override these methods if you want meaningful, efficient collections. Following is the code for the Contractor
class enhanced to use some getter methods we will use later in the lesson:
package info.java8;
/*
A Contractor class
*/
public class Contractor {
private String name = ""; // The Contractor Name.
private String location = ""; // City Location.
private String owner = ""; // The Customer Id.
Contractor() {
}
Contractor(String name, String location, String owner) {
this.name = name;
this.location = location;
this.owner = owner;
}
public boolean equals(Object obj) {
if (this==obj) { // Same reference variable so equal
return true;
}
if (!(obj instanceof Contractor)) // Make sure we have a Contractor instance
return false;
Contractor other = (Contractor) obj; // We need to cast to Contractor for comparison
if (location == null) { // Compare locations
if (other.location != null)
return false;
} else if (!location.equals(other.location))
return false;
if (name == null) { // Compare names
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public int hashCode() {
String hashKey = name + location;
return hashKey.hashCode();
}
// Getter methods
public String getName() {
return name;
}
public String getLocation() {
return location;
}
public String getOwner() {
return owner;
}
}
Save and compile the Contractor
class in directory c:\_Collections in the usual way.
Now lets create a TreeSet and store some Contractor
reference variables within it.
package info.java8;
/*
Simple class to sort some Contractor records with a TreeSet
*/
import java.util.*; // Import the java.util package
public class ContractorSort {
public static void main (String[] args) {
Contractor a = new Contractor();
Contractor b = new Contractor();
Contractor c = new Contractor("Contractor A", "Essex", "1234");
Contractor d = new Contractor("Contractor B", "Essex", "1234");
Contractor e = new Contractor("Contractor A", "Essex", "5678");
Set<Contractor> ts = new TreeSet<Contractor>();
ts.add(a);
ts.add(b);
ts.add(c);
ts.add(d);
ts.add(e);
int count = 0;
// Use enhanced for loop to iterate over the collection
for (Contractor con : ts) {
System.out.println("record: " + ++count + " " + con.getName() + con.getLocation()
+ con.getOwner()); // Print elements
}
}
}
Save, compile and run the ContractorSort
class in directory c:\_Collections in the usual way.
As the screenshot shows the code compiles fine but when we try to run it we get a ClassCastException
. The problem is that the TreeSet class has no idea how our
collection of Contractor
objects should be sorted. So when the second Contractor
is added we get the runtime error.
Ok, it seems we can't use an ordered, sorted collection for our Contractor
objects. What about something like an ArrayList that we can sort using the
Collections.sort()
method? Lets try it:
package info.java8;
/*
Simple class to sort some Contractor records using java.util.Collections.sort()
*/
import java.util.*; // Import the java.util package
public class ContractorSort2 {
public static void main (String[] args) {
Contractor a = new Contractor();
Contractor b = new Contractor();
Contractor c = new Contractor("Contractor A", "Essex", "1234");
Contractor d = new Contractor("Contractor B", "Essex", "1234");
Contractor e = new Contractor("Contractor A", "Essex", "5678");
List<Contractor> al = new ArrayList<Contractor>();
al.add(a);
al.add(b);
al.add(c);
al.add(d);
al.add(e);
Collections.sort(al);
int count = 0;
// Use enhanced for loop to iterate over the collection
for (Contractor con : al) {
System.out.println("index: " + count++ + " " + con.getName() + con.getLocation()
+ con.getOwner()); // Print elements
}
}
}
Save and compile the ContractorSort2
class in directory c:\_Collections in the usual way.
As the screenshot shows the code doesn't even compile. There are two sort()
methods in the java.util.Collections
class which expect the objects being sorted to either implement the
Comparable
or Comparator
interfaces, which our Contractor
class does not. As these scenarios show, the only way to sort our bespoke objects is to implement one of these
interfaces.
java.lang.Comparable<T>
Interface Top
The first way to allow our Contractor
class to be sorted is by using the java.lang.Comparable
interface. We need to implement the Comparable
interface and its only method
compareTo()
within our Contractor
class. The compareTo()
method returns a negative integer, zero, or a positive integer dependant upon this object being less than, equal to, or
greater than the specified object. The following code shows our updated Contractor
class, retrofitted to use the Comparable
interface:
package info.java8;
/*
A Contractor class
*/
public class Contractor implements Comparable<Contractor> { // Implement Comparable
private String name = "";
private String location = "";
private String owner = "";
Contractor() {
}
Contractor(String name, String location, String owner) {
this.name = name;
this.location = location;
this.owner = owner;
}
public boolean equals(Object obj) {
if (this==obj) {
return true;
}
if (!(obj instanceof Contractor))
return false;
Contractor other = (Contractor) obj;
if (location == null) {
if (other.location != null)
return false;
} else if (!location.equals(other.location))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public int hashCode() {
String hashKey = name + location;
return hashKey.hashCode();
}
// Getter methods
public String getName() {
return name;
}
public String getLocation() {
return location;
}
public String getOwner() {
return owner;
}
// Implement compareTo() method of Comparable interface
public int compareTo(Contractor c) {
String str1 = name + location;
String str2 = c.name + c.location;
return str1.compareTo(str2);
}
}
Save and compile the Contractor
class in directory c:\_Collections in the usual way.
Rerun the ContractorSort
class (which used a TreeSet) in directory c:\_Collections in the usual way.
As the screenshot shows the ContractorSort
class now works and sorts our collection by name and location. Remember that Sets do not allow duplicates so there are only 3 records shown.
Save, compile and run the ContractorSort2
class (which used the java.util.Collection.sort()
method) in directory c:\_Collections in the usual way.
As the screenshot shows the ContractorSort2
class now works and sorts our collection by name and location.
java.util.Comparator<T>
Interface Top
The second way to allow our Contractor
class to be sorted is by using the java.util.Comparator
interface. The Comparable
interface works fine but you have to change the
class you want sorted and is a 'one-trick pony' because you can only sort the collection in one way. But what about sorting classes we can't modify or we want to sort in different ways. Luckily for us Java
also has the java.util.Comparator<T>
interface, which allows us to sort a class without changing the code within that class, which also means we can have multiple sort sequences as well. All
we need to do is create a class that implements the java.util.Comparator<T>
interface and its compare()
method. So before we look into this lets recompile our Contractor
class as it was before we used the Comparable
interface:
package info.java8;
/*
A Contractor class
*/
public class Contractor {
private String name = ""; // The Contractor Name.
private String location = ""; // City Location.
private String owner = ""; // The Customer Id.
Contractor() {
}
Contractor(String name, String location, String owner) {
this.name = name;
this.location = location;
this.owner = owner;
}
public boolean equals(Object obj) {
if (this==obj) { // Same reference variable so equal
return true;
}
if (!(obj instanceof Contractor)) // Make sure we have a Contractor instance
return false;
Contractor other = (Contractor) obj; // We need to cast to Contractor for comparison
if (location == null) { // Compare locations
if (other.location != null)
return false;
} else if (!location.equals(other.location))
return false;
if (name == null) { // Compare names
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public int hashCode() {
String hashKey = name + location;
return hashKey.hashCode();
}
// Getter methods
public String getName() {
return name;
}
public String getLocation() {
return location;
}
public String getOwner() {
return owner;
}
}
Save and compile the Contractor
class in directory c:\_Collections in the usual way.
Now lets create some classes to implement the java.util.Comparator<T>
interface. The compare()
method of the java.util.Comparator<T>
interface returns an
integer in the same way as the compareTo()
method of the java.lang.Comparable<T>
interface. We can take advantage of this by letting the compareTo()
method do the
comparisons:
package info.java8;
/*
Simple class to implement Comparator to sort by Contractor.name
*/
import java.util.*;
public class ComparatorSortName implements Comparator<Contractor>{
public int compare(Contractor a, Contractor b) {
return a.getName().compareTo(b.getName());
}
}
/*
Simple class to implement Comparator to sort by Contractor.location
*/
import java.util.*;
public class ComparatorSortLocation implements Comparator<Contractor>{
public int compare(Contractor a, Contractor b) {
return a.getLocation().compareTo(b.getLocation());
}
}
/*
Simple class to implement Comparator to sort by Contractor.owner
*/
import java.util.*;
public class ComparatorSortOwner implements Comparator<Contractor>{
public int compare(Contractor a, Contractor b) {
return a.getOwner().compareTo(b.getOwner());
}
}
Save and compile the ComparatorSortName
, ComparatorSortLocation
and ComparatorSortOwner
classes in directory c:\_Collections in the usual way.
Lets create a new class to sort our Contractor
objects using our comparator classes ArrayList that we can sort using the Collections.sort()
method
that takes a comparator:
package info.java8;
/*
Simple class to sort some Contractor records using java.util.Collections.sort() with comparator
*/
import java.util.*; // Import the java.util package
public class ContractorSort3 {
public static void main (String[] args) {
Contractor a = new Contractor("Contractor C", "Essex", "7752");
Contractor b = new Contractor("Contractor F", "Surrey", "1234");
Contractor c = new Contractor("Contractor A", "Surrey", "1111");
Contractor d = new Contractor("Contractor B", "Kent", "6999");
Contractor e = new Contractor("Contractor A", "Kent", "5678");
List<Contractor> al = new ArrayList<Contractor>();
al.add(a);
al.add(b);
al.add(c);
al.add(d);
al.add(e);
ComparatorSortName csn = new ComparatorSortName();
Collections.sort(al, csn);
int count = 0;
// Use enhanced for loop to iterate over the collection
for (Contractor con : al) {
System.out.println("index: " + count++ + " " + con.getName() + con.getLocation()
+ con.getOwner()); // Print elements
}
ComparatorSortLocation csl = new ComparatorSortLocation();
Collections.sort(al, csl);
count = 0;
// Use enhanced for loop to iterate over the collection
for (Contractor con : al) {
System.out.println("index: " + count++ + " " + con.getName() + con.getLocation()
+ con.getOwner()); // Print elements
}
ComparatorSortOwner cso = new ComparatorSortOwner();
Collections.sort(al, cso);
count = 0;
// Use enhanced for loop to iterate over the collection
for (Contractor con : al) {
System.out.println("index: " + count++ + " " + con.getName() + con.getLocation()
+ con.getOwner()); // Print elements
}
}
}
Save and compile the ContractorSort3
class in directory c:\_Collections in the usual way.
As the screenshot shows we have used our three comparator classes to sort the collection in name, location and owner order and printed out the results.
Lesson 8 Complete
In this lesson we looked at sorting our collections using the Comparable
and Comparator
interfaces.
What's Next?
We start a new section on Streams with an overview of the Streams
API.