Java Collection with Comparators and Comparables

By | January 30, 2016

1. Try TreeSet with Comparator and Comparable

In Java, TreeSet is a member of the Java Collection Framework. Elements of the TreeSet are ordered by using their natural ordering, or by a Comparator. A Big-O notation of tree but not specific to Java refers to a pretty good website: bigocheatsheet.com

Java supports two interfaces to make a comparison: Comparable and Comparator. Each of these provides one method to be implemented by user.

Comparable and Comparator
java.lang.Comparable java.util.Comparator
int o1.compareTo(T o2) int compare(T o1, T o2)
-1, 0, or 1 as this object is less than, equal to, or greater than the specified object. Same as Comparable
Must modify the class whose instance you want to sort Build your own class separating from the class whose instance you want to sort.
Be able to create only one sort sequence. Many sort sequences.
Being implemented frequently to sort in the API by: String, Wrapper Class, Calendar… To sort instance of third-party classes.

Example

Car.java

/**
 * The Car class holds information and provides methods common to all cars.
 */
public class Car implements Comparable<Car> {

    private int serialNumber;
    private String manufacturerName;
    private int basePrice;

    /**
     * Construct a new Car.
     */
    public Car(int serialNumber, int basePrice, String manufacturerName) {
        this.serialNumber = serialNumber;
        this.basePrice = basePrice;
        this.manufacturerName = manufacturerName;
    }

    public int getBasePrice() {
        return basePrice;
    }

    public String getManufacturerName() {
        return manufacturerName;
    }

    public int compareTo(Car o) {
        return this.serialNumber - o.serialNumber;
    }

    @Override
    public String toString() {
        return this.serialNumber + " " + this.manufacturerName + " " + this.basePrice;
    }
}

CarSorted.java

public class CarSorted implements Comparator<Car> {
    public int compare(Car o1, Car o2) {
        // Compare with manufacturer name
        if (!o1.getManufacturerName().equals(o2.getManufacturerName())) {
            return o1.getManufacturerName().compareTo(o2.getManufacturerName());
        } else {
            // Compare with basePrice
            if (o1.getBasePrice() > o2.getBasePrice()) {
                return 1;
            } else if (o1.getBasePrice() < o2.getBasePrice()) {
                return -1;
            } else {
                return 0;
            }
        }
    }
}

CarTest.java

import java.util.Set;
import java.util.TreeSet;;

public class CarTest {

    public static void main(String[] args) {
        Set<Car> treeSet = new TreeSet<Car>();
        treeSet.add(new Car(8849480, 50000, "Toyota"));
        treeSet.add(new Car(7894848, 120000, "Honda"));
        treeSet.add(new Car(3747375, 70000, "Ford"));
        System.out.println("List of cars sorted by comparable: serialNumber");
        for (Car car : treeSet) {
            System.out.println(car.toString());
        }

        Set<Car> treeSet1 = new TreeSet<Car>(new CarSorted());
        treeSet1.add(new Car(8849480, 50000, "Toyota"));
        treeSet1.add(new Car(7894848, 120000, "Honda"));
        treeSet1.add(new Car(3747375, 70000, "Ford"));
        treeSet1.add(new Car(3747305, 60099, "Ford"));

        System.out.println("List of cars sorted by comparator:" +
                " manufacturerName, basePrice");
        for (Car car : treeSet1) {
            System.out.println(car.toString());
        }
    }
}

Output

List of cars sorted by comparable: serialNumber
3747375 Ford 70000
7894848 Honda 120000
8849480 Toyota 50000
List of cars sorted by comparator: manufacturerName, basePrice
3747305 Ford 60099
3747375 Ford 70000
7894848 Honda 120000
8849480 Toyota 50000

 

2. Update Sorting in Java 8 Lambda

In this example, we will try to present a Sorting strategies to sort a list of Countries.
2.1 Sorting strategies.
Apply different ordering solutions: behavior parameterization, anonymous classes, lambda
expressions, and method references.

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import static java.util.Comparator.comparing;

public class Sorting {
	public static void main(String... args) {
		List<Country> countries = Arrays.asList(
                new Country("US","United States","en"),
                new Country("AF","Afghanistan","af")
                );
		
		// 1. Pass code
		// [Country {code=AF, name=Afghanistan, localizationLang=af},
		//  Country {code=US, name=United States, localizationLang=en}]
		countries.sort(new CountryComparator());
		System.out.println(countries);
		
		// reshuffling things a little
		countries.set(0, new Country("ZA","South Africa","en"));
		
		// 2. Use an anonymous class
		// [Country {code=US, name=United States, localizationLang=en},
		//  Country {code=ZA, name=South Africa, localizationLang=en}]
		countries.sort( new Comparator<Country>() {
			@Override
			public int compare(Country c1, Country c2) {
				return c1.getCode().compareTo(c2.getCode());
			}
		});
		System.out.println(countries);
		
		// reshuffling things a little
		countries.set(0, new Country("GB","United Kingdom","en"));
		
		// 3. Use lambda expressions
		// [Country {code=GB, name=United Kingdom, localizationLang=en},
		//  Country {code=ZA, name=South Africa, localizationLang=en}]
		countries.sort((c1, c2) -> c1.getCode().compareTo(c2.getCode()));
		System.out.println(countries);
		
		// reshuffling things a little
		countries.set(1, new Country("BE","Belgium","nl"));
		
		// 4. Use method references.
		// which make your code slightly less verbose and more readable
		// assuming a static import of java.util.Comparator.comparing
		// [Country {code=BE, name=Belgium, localizationLang=nl}, 
		//  Country {code=GB, name=United Kingdom, localizationLang=en}]

		countries.sort(comparing(Country:: getCode));
		System.out.println(countries);
	}
	
	public static class Country {
		private String code;
		private String name;
		private String localizationLang;
		
		
		public Country(String code, String name, String localizationLang) {
			this.code = code;
			this.name = name;
			this.localizationLang = localizationLang;
		}
		
		public String getCode() {
			return code;
		}
		public void setCode(String code) {
			this.code = code;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public String getLocalizationLang() {
			return localizationLang;
		}
		public void setLocalizationLang(String localizationLang) {
			this.localizationLang = localizationLang;
		}
		
		@Override
		public String toString() {
			return "Country {code=" + code + ", name=" + name + ","
					+ " localizationLang=" + localizationLang + "}";
		}
		
	}
	public static class CountryComparator implements Comparator<Country> {
		@Override
		public int compare(Country o1, Country o2) {
			return o1.getCode().compareTo(o2.getCode());
		}
	}
}

2.2 Sort a list of countries by code in ascending order.

public static void main(String[] args) {
		List<Country> countries = Arrays.asList(
				new Country("US","United States","en"),
				new Country("AF","Afghanistan","af"),
				new Country("BE","Belgium","nl"),
				new Country("ZA","South Africa","en"),
				new Country("GB","United Kingdom","en")
				);
		
		Comparator<Country> countryNameComparator = 
                (c1, c2) -> (c1.getName().compareTo(c2.getName()));
		
		// Sort the countries in ascending order only.
		countries.sort(countryNameComparator);
		
		// Sort the countries in ascending order and using English language.
		List<Country> countrySortByAsc = countries.stream()
				.filter(c ->"en".equals(c.getLocalizationLang()))
				.sorted(countryNameComparator).collect(Collectors.toList());
}

Output

Afghanistan AF af
Belgium BE nl
South Africa ZA en
United Kingdom GB en
United States US en

[South Africa ZA en, United Kingdom GB en, United States US en]

2.3 Reversed sorting

     // To sort the records in a descending order, you can use reversed order
	List<Country> countrySortByDesc = countries.stream()
			.filter(c ->"en".equals(c.getLocalizationLang()))
			.sorted(countryNameComparator.reversed()).collect(Collectors.toList());

	// The same reversed order
	countries.sort(countryNameComparator.reversed());

Output

[United States US en, United Kingdom GB en, South Africa ZA en]

References:

  1. Raoul-Gabriel Urma, Mario Fusco, and Alan Mycrof, “Method References”, in Java 8 in Action: Lambdas, streams, and functional-style programming, pp. 82-92, Manning Publications, 2015.
  2. Oracle Java Document, “Object Ordering”. Accessed 23 April, 2016. https://docs.oracle.com/javase/tutorial/collections/interfaces/order.html
  3. “Interface Comparable,” Oracle Java Platform Standard Ed.7. Accessed Jan 30, 2016, https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html.
  4. “Interface Comparator,” Oracle Java Platform Standard Ed.7. Accessed Jan 30, 2016, https://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html.
  5. “Class TreeSet,” Oracle Java Platform Standard Ed.7. Accessed Jan 30, 2016, https://docs.oracle.com/javase/7/docs/api/java/util/TreeSet.html.
  6. “Interface Comparator” Oracle Java Platform Standard Ed.8. Accessed July 27, 2016, https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.