Welcome to our exploration of sorted maps using custom classes and comparators in Java. In today's lesson, we'll learn how to use custom classes as keys in sorted maps. This approach enhances data organization and access. With the addition of comparators, we can dictate the order in such maps.
A sorted map is a dictionary with its keys always in order. This arrangement makes operations like searching for keys within a range more efficient. In Java, we use the TreeMap
class to create sorted maps:
Java1import java.util.TreeMap; 2 3public class Main { 4 public static void main(String[] args) { 5 TreeMap<String, Integer> sMap = new TreeMap<>(); 6 sMap.put("a", 1); 7 sMap.put("b", 2); 8 sMap.put("c", 3); 9 10 System.out.println(sMap); // Outputs {a=1, b=2, c=3} 11 } 12}
Custom classes enable us to create objects that fit our data — for instance, a Person
class for employee information or a Book
class for a library database. In Java, classes are the blueprints for creating objects.
Consider this simple class, for example:
Java1public class Person { 2 private String name; 3 private int age; 4 5 public Person(String name, int age) { 6 this.name = name; 7 this.age = age; 8 } 9 10 public String getName() { 11 return name; 12 } 13 14 public int getAge() { 15 return age; 16 } 17 18 public static void main(String[] args) { 19 Person person = new Person("John Doe", 30); 20 System.out.println(person.getName()); // Outputs "John Doe" 21 System.out.println(person.getAge()); // Outputs 30 22 } 23}
Using custom classes as map keys helps organize complex multivariate keys in a sorted map. Consider the following example using the Person
class as a key in a sorted map (i.e., TreeMap
). However, this will not work yet.
Java1import java.util.TreeMap; 2 3public class Person { 4 private String name; 5 private int age; 6 7 public Person(String name, int age) { 8 this.name = name; 9 this.age = age; 10 } 11 12 @Override 13 public int hashCode() { 14 return Objects.hash(name, age); 15 } 16 17 // Getters and other methods... 18 19 public static void main(String[] args) { 20 TreeMap<Person, String> people = new TreeMap<>(); 21 22 Person john = new Person("John", 30); 23 Person alice = new Person("Alice", 25); 24 25 people.put(john, "Programmer"); 26 people.put(alice, "Designer"); 27 } 28}
We can see here that John is assigned the value "Programmer"
, and Alice is assigned the value "Designer."
However, this code will produce a ClassCastException
. The reason is that the TreeMap
needs a way to compare the Person
objects to maintain its order.
Java uses a comparator to determine the order of two keys. To make this comparison, we add the Comparable
interface or a custom Comparator
to our class. Without these methods, TreeMap
can't compare its Person
class keys. Here’s how to modify the Person
class to implement the Comparable
interface:
Java1import java.util.Objects; 2import java.util.TreeMap; 3 4public class Person implements Comparable<Person> { 5 private String name; 6 private int age; 7 8 public Person(String name, int age) { 9 this.name = name; 10 this.age = age; 11 } 12 13 @Override 14 public int compareTo(Person other) { 15 int ageCompare = Integer.compare(this.age, other.age); 16 if (ageCompare != 0) { 17 return ageCompare; 18 } 19 return this.name.compareTo(other.name); 20 } 21 22 @Override 23 public boolean equals(Object o) { 24 if (this == o) return true; 25 if (o == null || getClass() != o.getClass()) return false; 26 Person person = (Person) o; 27 return age == person.age && name.equals(person.name); 28 } 29 30 @Override 31 public int hashCode() { 32 return Objects.hash(name, age); 33 } 34 35 public static void main(String[] args) { 36 TreeMap<Person, String> people = new TreeMap<>(); 37 38 Person john = new Person("John", 30); 39 Person alice = new Person("Alice", 25); 40 41 people.put(john, "Programmer"); 42 people.put(alice, "Designer"); 43 44 for (Person person : people.keySet()) { 45 System.out.println(person.getName() + " is a " + people.get(person)); 46 } 47 // Output: 48 // Alice is a Designer 49 // John is a Programmer 50 } 51}
In the code above, we override the compareTo
, equals
, and hashCode
methods. The compareTo
method is required by the Comparable
interface, and it ensures that Person
objects are initially sorted by age and, if ages are the same, then by name. Overriding equals
and hashCode
ensures that Person
objects behave consistently when used in collections like TreeMap
. The hashCode
method returns an integer representation of an object for quick look-ups in hash-based collections.
We've explored how to use custom classes as keys in sorted maps and how comparators work in this context using the Comparable
interface. Now, prepare for some hands-on exercises to reinforce these concepts.