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]]

1 Comments:
Nice Post sir
Post a Comment
Subscribe to Post Comments [Atom]
<< Home