Comparator, sorting collections - 1

"Hi, Amigo!"

"Hi, Bilaabo!"

"Today we'll examine a small, but interesting and useful topic: sorting collections."

"Sorting? I've heard something about that."

"Long ago, every programmer had to be able to write sorting algorithms. Was able to and had to write them. But those days are over. Today, writing your own sorting code is considered bad form, just like rewriting anything else that has already been invented."

"In Java (and other programming languages), sorting is already implemented. Your task is to learn how to properly use what already exists."

"OK."

"The Collections helper class has a static sort method that is used to sort collections—or more precisely, lists. Elements in Maps and Sets don't have an order/index, so there's nothing to sort."

"Yes, I remember. I used this method once to sort a list of numbers."

"Great. But this method is much more powerful than it seems at first glance. It can sort not only numbers, but also any objects, based on any criteria. Two interfaces help the method do this: Comparable and Comparator."

"Sometimes you need to sort objects, not numbers. For example, suppose you have a list of people, and you want to sort them by age. We have the Comparable interface for this."

"Let me first show you an example, and then everything will become clearer:"

Example
public class Woman implements Comparable<Woman>
{
public int age;

public Woman(int age) {
this.age = age;
}

public int compareTo(Woman o)
{
return this.age - o.age;
}
}
An example of how it might be used:
public static void main(String[] args )
{
ArrayList<Woman> women = new ArrayList<Woman>();
women.add(new Woman(18));
women.add(new Woman(21));
women.add(new Woman(5));

Collections.sort(women);
}

"To sort objects, you must first know how to compare them. For this, we use Comparable. The Comparable interface is a generic, which means that it accepts a type argument. It has only one generic method: compareTo(T o). This method compares the current object (this) and an object passed as an argument (o). In other words, we need to implement this method in our class and then use it to compare the current object (this) with the passed object."

"And how does compareTo work? I expected that it would return true or false depending on whether the passed object was greater or smaller."

"Things are trickier here. The compareTo method doesn't return true/false. Instead, it returns an int. This is actually done for simplicity.

"When a computer needs to determine whether one number is greater than another, it simply subtracts the second number from the first and then looks at the result. If the result is 0, then the numbers are equal. If the result is less than zero, then the second number is greater. And if the result is greater than zero, then the first number is greater."

"The same logic applies here. According to the specification, the compareTo method must return zero if the compared objects are equal. If the compareTo method returns a number greater than zero, then our object is greater than the passed object. "If the compareTo method returns a number less than zero, then 'this' is less than the passed object."

"That's a little weird."

"Yes, but if you're comparing objects simply based on some numeric property, then you can just return the difference between them by subtracting one from the other. Just the way it was done in the example above."

public int compareTo(Woman o)
{
return this.age - o.age;
}

"I think I understand everything. But maybe not. But almost everything."

"Great. Now let's consider a more practical problem. Suppose you've written a cool website for making women's clothing in China. You use a Woman class to describe your customers. You even made a webpage with a table where you can see them all. But there's a problem…"

"Your Woman object contains not only an age, but also a whole bunch of other data: first name, last name, height, weight, number of children, etc."

"The table of users has lots of columns, and here's the question: how do you sort your users by the various criteria? By weight, by age, by last name?"

"Hmm. Yeah, I often see tables that let you sort by column. So, how do you do that?"

"For this, we have the second interface I wanted to tell you about today: the Comparator interface. It also has a compare method, but it takes two arguments, not one: int compare(T o1, T o2). Here's how it works:"

Example
public class Woman
{
public int age;
public int childrenCount;
public int weight;
public int height;
public String name;

public Woman(int age) {
this.age = age;
}
}
An example of how it might be used:
public static void main(String[] args )
{
ArrayList<Woman> women = new ArrayList<Woman>();
women.add(new Woman(18));
women.add(new Woman(21));
women.add(new Woman(5));

Comparator<Woman> compareByHeight = new Comparator<Woman>() {
public int compare(Woman o1, Woman o2) {
return o1.height - o2.height;
}
};

Collections.sort(women, compareByHeight);
}

"The Comparator interface doesn't hide the object comparison logic inside the class of the objects being compared. Instead, it is implemented in a separate class."

"So, I could make several classes that implement the Comparator interface, and have each of them compare different properties? Weight in one, age in another, and height in a third?"

"Yes, it's very simple and convenient."

"We just call the Collections.sort method, passing a list of objects and another special object as the second argument, which implements the Comparator interface and tells you how to correctly compare pairs of objects in the sorting process."

"Hmm. I think I understand everything. Let me give it a try. Let's say I need to sort users by weight. It would be something like this:"

Example of sorting users by weight:
Comparator<Woman> compareByWeight = new Comparator<Woman>() {
public int compare(Woman o1, Woman o2) {
return o1.weight - o2.weight;
}
};

Collections.sort(women, compareByWeight);

"Yes, exactly."

"Great. But what if I want to sort in reverse order?"

"Think about it. The answer is very simple!"

"I've got it! Like this:"

Sorting in ascending order:
return o1.weight - o2.weight;
Sorting in decreasing order:
return o2.weight – o1.weight;

"Right. Well done."

"And if I want to sort by last name? How do I sort strings, Bilaabo?"

"The String class already implements the compareTo method. You just need to call it:"

Example of sorting users by name:
Comparator<Woman> compareByName = new Comparator<Woman>() {
public int compare(Woman o1, Woman o2) {
return o1.name.compareTo(o2.name);
}
};

Collections.sort(women, compareByName);

"That was a great lesson, Bilaabo. Thank you very much."

"And thank you to you, my friend!"