Google

May 19, 2014

Different ways to sort a collection of objects in pre and post Java 8

The object we are going to sort is a Person.

public class Person  {
 
 public enum Gender {FEMALE, MALE};

 private String name;
 private Integer age;
 private Gender gender;

 public Person(String name, Integer age, Gender gender) {
  this.name = name;
  this.age = age;
  this.gender = gender;
 }

 //getter, setter, equals(...), and hashCode() methods skipped

 @Override
 public String toString() {
  return "Person [name=" + name + ", age=" + age + ", gender=" + gender + "]";
 }
}


Option 1: Writing your own Comparator implementation. This can be done as an anonymous inner class instead of a separate class.


import java.util.Comparator;

public class PersonComparator implements Comparator<Person> {

 @Override
 public int compare(Person o1, Person o2) {
  
  //by gender first
  int i1 = o1.getGender().compareTo(o2.getGender());
  if (i1 != 0) return i1;
  
  //by name next
  int i2 =  o1.getName().compareTo(o2.getName());
  if (i2 != 0) return i2;
  
  //by age
  return o1.getAge().compareTo(o2.getAge());
 }
}


The test class

import java.util.ArrayList;
import java.util.List;

public class PersonTest {
 
 public static void main(String[] args) {
  
  List<Person> people = new ArrayList<>();
  people.add(new Person("John", 35, Person.Gender.MALE));
  people.add(new Person("John", 32, Person.Gender.MALE));
  people.add(new Person("Simone", 30, Person.Gender.FEMALE));
  people.add(new Person("Shawn", 30, Person.Gender.MALE));
  
  System.out.println("before sorting = "  + people);
  people.sort(new PersonComparator());
  System.out.println("after sorting = "  + people);
 
 }
}


Option 2: The Option 1 is not bad, but the the moment you need to handle null element values, the PersonComparator will have more code. One of the best practices in Java is "Don't reinvent the wheel". So, let's use the BeanComparator,NullComparator, and ComparatorChain from the Apache commons library commons-beanutils -> commons-beanutils-bean-collections that uses reflection. The example below also handles null values.

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.comparators.ComparatorChain;
import org.apache.commons.collections.comparators.NullComparator;

public class PersonTest {
 
 public static void main(String[] args) {
  
  List<Person> people = new ArrayList<>();
  people.add(new Person("John", 35, Person.Gender.MALE));
  people.add(new Person("John", 32, Person.Gender.MALE));
  people.add(new Person("Simone", 30, Person.Gender.FEMALE));
  people.add(new Person("Shawn", 30, Person.Gender.MALE));
  people.add(new Person("Shawn", 30, null));
  
  System.out.println("before sorting = "  + people);
  
  //Apache commons-beanutils.commons-beanutils-bean-collections
  ComparatorChain comparatorChain = new ComparatorChain();
  //null is compared s lower
  comparatorChain.addComparator(new BeanComparator("gender", new NullComparator(false)));
  //null is compared as higher
  comparatorChain.addComparator(new BeanComparator("name", new NullComparator(true)));
  comparatorChain.addComparator(new BeanComparator("age", new NullComparator(true)));
  people.sort(comparatorChain);
  
  System.out.println("after sorting = "  + people);
 
 }
}





Option 3: Using the Google Gauva library to sort the collection in functional programming style.

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Ordering;

public class PersonTest {
 
 public static void main(String[] args) {
  
  List<Person> people = new ArrayList<>();
  people.add(new Person("John", 35, Person.Gender.MALE));
  people.add(new Person("John", 32, Person.Gender.MALE));
  people.add(new Person("Simone", 30, Person.Gender.FEMALE));
  people.add(new Person("Shawn", 30, Person.Gender.MALE));
  people.add(new Person("Shawn", 30, null));
  
  System.out.println("before sorting = "  + people);
  
  //anonymous inner class using the Google Gauva library
  people.sort(new Comparator<Person>() {

   @Override
   public int compare(Person o1, Person o2) {
    return ComparisonChain.start()
          .compare(o1.getGender(), o2.getGender(),  Ordering.natural().nullsFirst())
          .compare(o1.getName(), o2.getName(), Ordering.natural().nullsLast())
          .compare(o1.getAge(), o2.getAge(), Ordering.natural().nullsLast())
          .result();
   }

  
  });
  
  System.out.println("after sorting = "  + people);
 
 }
}


Option 4: If you are using Java 8, using the functional programming approach.

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class PersonTest {
 
 public static void main(String[] args) {
  
  List<Person> people = new ArrayList<>();
  people.add(new Person("John", 35, Person.Gender.MALE));
  people.add(new Person("John", 32, Person.Gender.MALE));
  people.add(new Person("Simone", 30, Person.Gender.FEMALE));
  people.add(new Person("Shawn", 30, Person.Gender.MALE));
  people.add(new Person("Shawn", 30, null));
  
  System.out.println("before sorting = "  + people);
  
  //java 8 approach fro multi-fields
  Comparator<Person> multiFieldComparator = 
            Comparator.comparing(Person::getGender, Comparator.nullsFirst(Comparator.naturalOrder()))
                      .thenComparing(Person::getName, Comparator.nullsLast(Comparator.naturalOrder()))
                      .thenComparing(Person::getAge,  Comparator.nullsLast(Comparator.naturalOrder())) ;
  
  people.sort(multiFieldComparator);
  System.out.println("after sorting = "  + people);
 
 }
}


Option 5: If you are using Java 8, using parallel processing. Very similar to option 4, but processed in parallel with minor changes.

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class PersonTest {
 
 public static void main(String[] args) {
  
  List<Person> people = new ArrayList<>();
  people.add(new Person("John", 35, Person.Gender.MALE));
  people.add(new Person("John", 32, Person.Gender.MALE));
  people.add(new Person("Simone", 30, Person.Gender.FEMALE));
  people.add(new Person("Shawn", 30, Person.Gender.MALE));
  people.add(new Person("Shawn", 30, null));
  
  System.out.println("before sorting = "  + people);
  
  Comparator<Person> multiFieldComparator = 
          Comparator.comparing(Person::getGender, Comparator.nullsFirst(Comparator.naturalOrder()))
                    .thenComparing(Person::getName, Comparator.nullsLast(Comparator.naturalOrder()))
                    .thenComparing(Person::getAge,  Comparator.nullsLast(Comparator.naturalOrder())) ;
  
     //parallel() processing using Fork/Join 
  List<Object> sortedPeople = people.stream()
                                          .parallel()
                                          .sorted(multiFieldComparator)
                                          .collect(Collectors.toList());
            
  System.out.println("after sorting = "  + sortedPeople);
 
 }
}


Output:

before sorting = [Person [name=John, age=35, gender=MALE], Person [name=John, age=32, gender=MALE], Person [name=Simone, age=30, gender=FEMALE], Person [name=Shawn, age=30, gender=MALE], Person [name=Shawn, age=30, gender=null]]

after sorting = [Person [name=Shawn, age=30, gender=null], Person [name=Simone, age=30, gender=FEMALE], Person [name=John, age=32, gender=MALE], Person [name=John, age=35, gender=MALE], Person [name=Shawn, age=30, gender=MALE]]

Labels: ,

1 Comments:

Blogger Unknown said...

Nice Post sir

4:27 AM, May 20, 2014  

Post a Comment

Subscribe to Post Comments [Atom]

<< Home