316

Assume you have some objects which have several fields they can be compared by:

public class Person {

    private String firstName;
    private String lastName;
    private String age;

    /* Constructors */

    /* Methods */

}

So in this example, when you ask if:

a.compareTo(b) > 0

you might be asking if a's last name comes before b's, or if a is older than b, etc...

What is the cleanest way to enable multiple comparison between these kinds of objects without adding unnecessary clutter or overhead?

  • java.lang.Comparable interface allows comparison by one field only
  • Adding numerous compare methods (i.e. compareByFirstName(), compareByAge(), etc...) is cluttered in my opinion.

So what is the best way to go about this?

Machavity
  • 30,841
  • 27
  • 92
  • 100
Yuval Adam
  • 161,610
  • 92
  • 305
  • 395
  • 5
    why is this a cw? It's a perfectly valid programming question. – Elie Dec 15 '08 at 20:42
  • 3
    Are you aware that Comparable allows comparison by as many fields as you like? – DJClayworth Dec 15 '08 at 20:47
  • possible duplicate of [Using Comparable for multiple dynamic fields of VO in java](http://stackoverflow.com/questions/16206629/using-comparable-for-multiple-dynamic-fields-of-vo-in-java) – eugenevd Feb 27 '14 at 11:51

23 Answers23

535

With Java 8:

Comparator.comparing((Person p)->p.firstName)
          .thenComparing(p->p.lastName)
          .thenComparingInt(p->p.age);

If you have accessor methods:

Comparator.comparing(Person::getFirstName)
          .thenComparing(Person::getLastName)
          .thenComparingInt(Person::getAge);

If a class implements Comparable then such comparator may be used in compareTo method:

@Override
public int compareTo(Person o){
    return Comparator.comparing(Person::getFirstName)
              .thenComparing(Person::getLastName)
              .thenComparingInt(Person::getAge)
              .compare(this, o);
}
Blaine
  • 652
  • 8
  • 11
Display Name
  • 76
  • 2
  • 2
  • 5
  • 14
    Especially the cast `(Person p)` is important for chained comparators. – membersound Oct 06 '17 at 07:42
  • 8
    How efficient it is when comparing a large number of objects, e.g. when sorting? Does it have to create new `Comparator` instances in every call? – jjurm Nov 20 '17 at 22:38
  • 5
    I get a NullPointerException when one of the fields I am comparing is null, like a String. Is there anyway to keep this compare format but allow it to be null safe? – rveach May 04 '18 at 21:15
  • 6
    @jjurm `.thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))` - first field selector, then comparator – gavenkoa May 30 '18 at 14:22
  • 6
    @jjurm when you use it in `compareTo` as shown above the `Comparator` is created each time the method is called. You could prevent this by storing the comparator in a private static final field. – Gandalf Oct 23 '18 at 22:25
  • 3
    @membersound: `(Person p)` is not a cast. It's the type declaration of the argument of the lambda expression. – Stefan Steinegger Dec 02 '20 at 14:53
181

You should implement Comparable <Person>. Assuming all fields will not be null (for simplicity sake), that age is an int, and compare ranking is first, last, age, the compareTo method is quite simple:

public int compareTo(Person other) {
    int i = firstName.compareTo(other.firstName);
    if (i != 0) return i;

    i = lastName.compareTo(other.lastName);
    if (i != 0) return i;

    return Integer.compare(age, other.age);
}
Alan
  • 1,510
  • 1
  • 18
  • 35
Steve Kuo
  • 61,876
  • 75
  • 195
  • 257
  • 10
    if you implement Comparable then the method is compareTo(Person p).. it seems that this answer was mixed up with Comparator's compare method – Mike Dec 14 '12 at 12:59
  • 6
    This is not recommended. Use Comparator when you have multiple fields. – indika Apr 20 '14 at 04:09
  • 1
    that's the best solution at the moment, (better than more comparators) – Vasile Surdu Jun 06 '14 at 18:41
  • 5
    @indika, I'm curious: why is this not recommended? Comparing using more than one property seems perfectly fine to me. – ars-longa-vita-brevis Sep 25 '14 at 22:31
  • 4
    @ars-longa-vita-brevis, If you are using Comparable then sorting logic must be in the same class whose objects are being sorted so this is called natural ordering of objects. With the use of Comparator you can write custom sorting logic outside the Person class. If you want to compare Person objects only by its first name or last name, you cant use this logic. You have to write it again, – indika Sep 26 '14 at 06:38
130

(from Ways to sort lists of objects in Java based on multiple fields)

Working code in this gist

Using Java 8 lambda's (added April 10, 2019)

Java 8 solves this nicely by lambda's (though Guava and Apache Commons might still offer more flexibility):

Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
            .thenComparing(Report::getStudentNumber)
            .thenComparing(Report::getSchool));

Thanks to @gaoagong's answer below.

Messy and convoluted: Sorting by hand

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        int sizeCmp = p1.size.compareTo(p2.size);  
        if (sizeCmp != 0) {  
            return sizeCmp;  
        }  
        int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings);  
        if (nrOfToppingsCmp != 0) {  
            return nrOfToppingsCmp;  
        }  
        return p1.name.compareTo(p2.name);  
    }  
});  

This requires a lot of typing, maintenance and is error prone.

The reflective way: Sorting with BeanComparator

ComparatorChain chain = new ComparatorChain(Arrays.asList(
   new BeanComparator("size"), 
   new BeanComparator("nrOfToppings"), 
   new BeanComparator("name")));

Collections.sort(pizzas, chain);  

Obviously this is more concise, but even more error prone as you lose your direct reference to the fields by using Strings instead (no typesafety, auto-refactorings). Now if a field is renamed, the compiler won’t even report a problem. Moreover, because this solution uses reflection, the sorting is much slower.

Getting there: Sorting with Google Guava’s ComparisonChain

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result();  
        // or in case the fields can be null:  
        /* 
        return ComparisonChain.start() 
           .compare(p1.size, p2.size, Ordering.natural().nullsLast()) 
           .compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast()) 
           .compare(p1.name, p2.name, Ordering.natural().nullsLast()) 
           .result(); 
        */  
    }  
});  

This is much better, but requires some boiler plate code for the most common use case: null-values should be valued less by default. For null-fields, you have to provide an extra directive to Guava what to do in that case. This is a flexible mechanism if you want to do something specific, but often you want the default case (ie. 1, a, b, z, null).

Sorting with Apache Commons CompareToBuilder

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison();  
    }  
});  

Like Guava’s ComparisonChain, this library class sorts easily on multiple fields, but also defines default behavior for null values (ie. 1, a, b, z, null). However, you can’t specify anything else either, unless you provide your own Comparator.

Thus

Ultimately it comes down to flavor and the need for flexibility (Guava’s ComparisonChain) vs. concise code (Apache’s CompareToBuilder).

Bonus method

I found a nice solution that combines multiple comparators in order of priority on CodeReview in a MultiComparator:

class MultiComparator<T> implements Comparator<T> {
    private final List<Comparator<T>> comparators;

    public MultiComparator(List<Comparator<? super T>> comparators) {
        this.comparators = comparators;
    }

    public MultiComparator(Comparator<? super T>... comparators) {
        this(Arrays.asList(comparators));
    }

    public int compare(T o1, T o2) {
        for (Comparator<T> c : comparators) {
            int result = c.compare(o1, o2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }

    public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
        Collections.sort(list, new MultiComparator<T>(comparators));
    }
}

Ofcourse Apache Commons Collections has a util for this already:

ComparatorUtils.chainedComparator(comparatorCollection)

Collections.sort(list, ComparatorUtils.chainedComparator(comparators));
Benny Bottema
  • 11,111
  • 10
  • 71
  • 96
90

You can implement a Comparator which compares two Person objects, and you can examine as many of the fields as you like. You can put in a variable in your comparator that tells it which field to compare to, although it would probably be simpler to just write multiple comparators.

jediz
  • 4,459
  • 5
  • 36
  • 41
Elie
  • 13,693
  • 23
  • 74
  • 128
  • 6
    I Actually prefer the idea of using a single Comparator. I don't think this answer is wrong, but anyone reading it should definitely check Steve Kuo answer below. – Felipe Leão Nov 13 '14 at 12:15
  • The multiple comparators was only if you want different comparison methods that are not a function of the data itself - i.e. sometimes you want to compare by name, other times by age, etc. To compare by multiple fields at the same time, only one comparator would be necessary. – Elie Nov 13 '14 at 17:13
23

@Patrick To sort more than one field consecutively try ComparatorChain

A ComparatorChain is a Comparator that wraps one or more Comparators in sequence. The ComparatorChain calls each Comparator in sequence until either 1) any single Comparator returns a non-zero result (and that result is then returned), or 2) the ComparatorChain is exhausted (and zero is returned). This type of sorting is very similar to multi-column sorting in SQL, and this class allows Java classes to emulate that kind of behaviour when sorting a List.

To further facilitate SQL-like sorting, the order of any single Comparator in the list can >be reversed.

Calling a method that adds new Comparators or changes the ascend/descend sort after compare(Object, Object) has been called will result in an UnsupportedOperationException. However, take care to not alter the underlying List of Comparators or the BitSet that defines the sort order.

Instances of ComparatorChain are not synchronized. The class is not thread-safe at construction time, but it is thread-safe to perform multiple comparisons after all the setup operations are complete.

Community
  • 1
  • 1
Nigel_V_Thomas
  • 907
  • 13
  • 27
21

Another option you can always consider is Apache Commons. It provides a lot of options.

import org.apache.commons.lang3.builder.CompareToBuilder;

Ex:

public int compare(Person a, Person b){

   return new CompareToBuilder()
     .append(a.getName(), b.getName())
     .append(a.getAddress(), b.getAddress())
     .toComparison();
}
Alberto M
  • 1,608
  • 1
  • 18
  • 42
Xeroiris
  • 1
  • 1
  • 2
14
import com.google.common.collect.ComparisonChain;

/**
 * @author radler
 * Class Description ...
 */
public class Attribute implements Comparable<Attribute> {

    private String type;
    private String value;

    public String getType() { return type; }
    public void setType(String type) { this.type = type; }

    public String getValue() { return value; }
    public void setValue(String value) { this.value = value; }

    @Override
    public String toString() {
        return "Attribute [type=" + type + ", value=" + value + "]";
    }

    @Override
    public int compareTo(Attribute that) {
        return ComparisonChain.start()
            .compare(this.type, that.type)
            .compare(this.value, that.value)
            .result();
    }

}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Ran Adler
  • 3,587
  • 30
  • 27
13

You can also have a look at Enum that implements Comparator.

http://tobega.blogspot.com/2008/05/beautiful-enums.html

e.g.

Collections.sort(myChildren, Child.Order.ByAge.descending());
Alberto M
  • 1,608
  • 1
  • 18
  • 42
Boune
  • 1,063
  • 2
  • 10
  • 15
9

For those able to use the Java 8 streaming API, there is a neater approach that is well documented here: Lambdas and sorting

I was looking for the equivalent of the C# LINQ:

.ThenBy(...)

I found the mechanism in Java 8 on the Comparator:

.thenComparing(...)

So here is the snippet that demonstrates the algorithm.

    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

Check out the link above for a neater way and an explanation about how Java's type inference makes it a bit more clunky to define compared to LINQ.

Here is the full unit test for reference:

@Test
public void testChainedSorting()
{
    // Create the collection of people:
    ArrayList<Person> people = new ArrayList<>();
    people.add(new Person("Dan", 4));
    people.add(new Person("Andi", 2));
    people.add(new Person("Bob", 42));
    people.add(new Person("Debby", 3));
    people.add(new Person("Bob", 72));
    people.add(new Person("Barry", 20));
    people.add(new Person("Cathy", 40));
    people.add(new Person("Bob", 40));
    people.add(new Person("Barry", 50));

    // Define chained comparators:
    // Great article explaining this and how to make it even neater:
    // http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

    // Sort the stream:
    Stream<Person> personStream = people.stream().sorted(comparator);

    // Make sure that the output is as expected:
    List<Person> sortedPeople = personStream.collect(Collectors.toList());
    Assert.assertEquals("Andi",  sortedPeople.get(0).name); Assert.assertEquals(2,  sortedPeople.get(0).age);
    Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
    Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
    Assert.assertEquals("Bob",   sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
    Assert.assertEquals("Bob",   sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
    Assert.assertEquals("Bob",   sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
    Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
    Assert.assertEquals("Dan",   sortedPeople.get(7).name); Assert.assertEquals(4,  sortedPeople.get(7).age);
    Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3,  sortedPeople.get(8).age);
    // Andi     : 2
    // Barry    : 20
    // Barry    : 50
    // Bob      : 40
    // Bob      : 42
    // Bob      : 72
    // Cathy    : 40
    // Dan      : 4
    // Debby    : 3
}

/**
 * A person in our system.
 */
public static class Person
{
    /**
     * Creates a new person.
     * @param name The name of the person.
     * @param age The age of the person.
     */
    public Person(String name, int age)
    {
        this.age = age;
        this.name = name;
    }

    /**
     * The name of the person.
     */
    public String name;

    /**
     * The age of the person.
     */
    public int age;

    @Override
    public String toString()
    {
        if (name == null) return super.toString();
        else return String.format("%s : %d", this.name, this.age);
    }
}
Luke Machowski
  • 3,983
  • 2
  • 31
  • 28
8

Writing a Comparator manually for such an use case is a terrible solution IMO. Such ad hoc approaches have many drawbacks:

  • No code reuse. Violates DRY.
  • Boilerplate.
  • Increased possibility of errors.

So what's the solution?

First some theory.

Let us denote the proposition "type A supports comparison" by Ord A. (From program perspective, you can think of Ord A as an object containing logic for comparing two As. Yes, just like Comparator.)

Now, if Ord A and Ord B, then their composite (A, B) should also support comparison. i.e. Ord (A, B). If Ord A, Ord B, and Ord C, then Ord (A, B, C).

We can extend this argument to arbitrary arity, and say:

Ord A, Ord B, Ord C, ..., Ord ZOrd (A, B, C, .., Z)

Let's call this statement 1.

The comparison of the composites will work just as you described in your question: the first comparison will be tried first, then the next one, then the next, and so on.

That's the first part of our solution. Now the second part.

If you know that Ord A, and know how to transform B to A (call that transformation function f), then you can also have Ord B. How? Well, when the two B instances are to be compared, you first transform them to A using f and then apply Ord A.

Here, we are mapping the transformation B → A to Ord A → Ord B. This is known as contravariant mapping (or comap for short).

Ord A, (B → A)comap Ord B

Let's call this statement 2.


Now let's apply this to your example.

You have a data type named Person that comprises three fields of type String.

  • We know that Ord String. By statement 1, Ord (String, String, String).

  • We can easily write a function from Person to (String, String, String). (Just return the three fields.) Since we know Ord (String, String, String) and Person → (String, String, String), by statement 2, we can use comap to get Ord Person.

QED.


How do I implement all these concepts?

The good news is you don't have to. There already exists a library which implements all the ideas described in this post. (If you are curious how these are implemented, you can look under the hood.)

This is how the code will look with it:

Ord<Person> personOrd = 
 p3Ord(stringOrd, stringOrd, stringOrd).comap(
   new F<Person, P3<String, String, String>>() {
     public P3<String, String, String> f(Person x) {
       return p(x.getFirstName(), x.getLastname(), x.getAge());
     }
   }
 );

Explanation:

  • stringOrd is an object of type Ord<String>. This corresponds to our original "supports comparison" proposition.
  • p3Ord is a method that takes Ord<A>, Ord<B>, Ord<C>, and returns Ord<P3<A, B, C>>. This corresponds to statement 1. (P3 stands for product with three elements. Product is an algebraic term for composites.)
  • comap corresponds to well, comap.
  • F<A, B> represents a transformation function A → B.
  • p is a factory method for creating products.
  • The whole expression corresponds to statement 2.

Hope that helps.

missingfaktor
  • 90,905
  • 62
  • 285
  • 365
5

Instead of comparison methods you may want to just define several types of "Comparator" subclasses inside the Person class. That way you can pass them into standard Collections sorting methods.

Marc Novakowski
  • 44,628
  • 11
  • 58
  • 63
5
//Following is the example in jdk 1.8
package com;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

class User {
    private String firstName;
    private String lastName;
    private Integer age;

    public Integer getAge() {
        return age;
    }

    public User setAge(Integer age) {
        this.age = age;
        return this;
    }

    public String getFirstName() {
        return firstName;
    }

    public User setFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public String getLastName() {
        return lastName;
    }

    public User setLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

}

public class MultiFieldsComparision {

    public static void main(String[] args) {
        List<User> users = new ArrayList<User>();

        User u1 = new User().setFirstName("Pawan").setLastName("Singh").setAge(38);
        User u2 = new User().setFirstName("Pawan").setLastName("Payal").setAge(37);
        User u3 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(60);
        User u4 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(43);
        User u5 = new User().setFirstName("Pawan").setLastName("Chamoli").setAge(44);
        User u6 = new User().setFirstName("Pawan").setLastName("Singh").setAge(5);

        users.add(u1);
        users.add(u2);
        users.add(u3);
        users.add(u4);
        users.add(u5);
        users.add(u6);

        System.out.println("****** Before Sorting ******");

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

        System.out.println("****** Aftre Sorting ******");

        users.sort(
                Comparator.comparing(User::getFirstName).thenComparing(User::getLastName).thenComparing(User::getAge));

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

    }

}
Pawan
  • 1
  • 1
  • 2
4

Code implementation of the same is here if we have to sort the Person object based on multiple fields.

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Person {

private String firstName;
private String lastName;
private int age;

public String getFirstName() {
    return firstName;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

public String getLastName() {
    return lastName;
}

public void setLastName(String lastName) {
    this.lastName = lastName;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

public Person(String firstName, String lastName, int age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
}


static class PersonSortingComparator implements Comparator<Person> {

    @Override
    public int compare(Person person1, Person person2) {

        // for first name comparison
        int firstNameCompare = person1.getFirstName().compareTo(person2.getFirstName());

        // for last name comparison
        int lastNameCompare = person1.getLastName().compareTo(person2.getLastName());

        // for last name comparison
        int ageCompare = person1.getAge() - person2.getAge();

        // Now comparing
        if (firstNameCompare == 0) {
            if (lastNameCompare == 0) {
                return ageCompare;
            }
            return lastNameCompare;
        }
        return firstNameCompare;
    }
}

public static void main(String[] args) {
    Person person1 = new Person("Ajay", "Kumar", 27);
    Person person2 = new Person("Ajay","Gupta", 23);
    Person person3 = new Person("Ajay","Kumar", 22);


    ArrayList<Person> persons = new ArrayList<>();
    persons.add(person1);
    persons.add(person2);
    persons.add(person3);


    System.out.println("Before Sorting:\n");
    for (Person person : persons) {
        System.out.println(person.firstName + " " + person.lastName + " " + person.age);
    }

    Collections.sort(persons, new PersonSortingComparator());

    System.out.println("After Sorting:\n");
    for (Person person : persons) {
        System.out.println(person.firstName + " " + person.lastName + " " + person.age);
    }
}

}
Sachchit Bansal
  • 486
  • 5
  • 11
3

I think it'd be more confusing if your comparison algorithm were "clever". I'd go with the numerous comparison methods you suggested.

The only exception for me would be equality. For unit testing, it's been useful to me to override the .Equals (in .net) in order to determine if several fields are equal between two objects (and not that the references are equal).

Michael Haren
  • 105,752
  • 40
  • 168
  • 205
3

If there are multiple ways a user might order person, you could also have multiple Comparators setup as constants somewhere. Most of the sort operations and sorted collections take a comparator as a parameter.

sblundy
  • 60,628
  • 22
  • 121
  • 123
3

Java 8 through lambda way we can compare by method reference. Student POJO

public class Student {
int id;
String firstName;
String lastName;
String subject;

public Student(int id, String firstName, String lastName, String subject) {
    this.id = id;
    this.firstName = firstName;
    this.lastName = lastName;
    this.subject = subject;
}
enter code here

Now we can sort based on

1. id->FirstName->LastName->Subject 2. Subject->id->FirstName->LastName

We will use Comparator in array Stream

public class TestComprator {
public static void main(String[] args) {
    Student s1= new Student(108, "James", "Testo", "Physics");
    Student s2= new Student(101, "Fundu", "Barito", "Chem");
    Student s3= new Student(105, "Sindhu", "Sharan", "Math");
    Student s4= new Student(98, "Rechel", "Stephen", "Physics");
    System.out.printf("----------id->FirstName->LastName->Subject-------------");
    Arrays.asList(s1,s2,s3,s4).stream()
            .sorted(Comparator.comparing(Student::getId)
                    .thenComparing(Student::getFirstName)
                .thenComparing(Student::getLastName)
                .thenComparing(Student::getSubject))
            .forEach(System.out::println);

    System.out.printf("----Subject->id->FirstName->LastName ------\n");
    Arrays.asList(s1,s2,s3,s4).stream()
            .sorted(Comparator. comparing(Student::getSubject)
                    .thenComparing(Student::getId)
                    .thenComparing(Student::getFirstName)
                    .thenComparing(Student::getLastName)
                   )
            .forEach(System.out::println);
}

}

Output:-

`----------id->FirstName->LastName->Subject-------------
Student{id=98, firstName='Rechel', lastName='Stephen', subject='Physics'}
Student{id=101, firstName='Fundu', lastName='Barito', subject='Chem'}
Student{id=105, firstName='Sindhu', lastName='Sharan', subject='Math'}
Student{id=108, firstName='James', lastName='Testo', subject='Physics'}
 ----Subject->id->FirstName->LastName ------
Student{id=101, firstName='Fundu', lastName='Barito', subject='Chem'}
Student{id=105, firstName='Sindhu', lastName='Sharan', subject='Math'}
Student{id=98, firstName='Rechel', lastName='Stephen', subject='Physics'}
Student{id=108, firstName='James', lastName='Testo', subject='Physics'}
Abhilash Ranjan
  • 715
  • 8
  • 8
2
//here threshold,buyRange,targetPercentage are three keys on that i have sorted my arraylist 
final Comparator<BasicDBObject> 

    sortOrder = new Comparator<BasicDBObject>() {
                    public int compare(BasicDBObject e1, BasicDBObject e2) {
                        int threshold = new Double(e1.getDouble("threshold"))
                        .compareTo(new Double(e2.getDouble("threshold")));
                        if (threshold != 0)
                            return threshold;

                        int buyRange = new Double(e1.getDouble("buyRange"))
                        .compareTo(new Double(e2.getDouble("buyRange")));
                        if (buyRange != 0)
                            return buyRange;

                        return (new Double(e1.getDouble("targetPercentage")) < new Double(
                                e2.getDouble("targetPercentage")) ? -1 : (new Double(
                                        e1.getDouble("targetPercentage")) == new Double(
                                                e2.getDouble("targetPercentage")) ? 0 : 1));
                    }
                };
                Collections.sort(objectList, sortOrder);
2

Better late than never - if you're looking for unnecessary clutter or overhead then it's hard to beat the following in terms of least code/fast execution at the same time.

Data class:

public class MyData {
    int id;
    boolean relevant;
    String name;
    float value;
}

Comparator:

public class MultiFieldComparator implements Comparator<MyData> {
    @Override
    public int compare(MyData dataA, MyData dataB) {
        int result;
        if((result = Integer.compare(dataA.id, dataB.id)) == 0 &&
           (result = Boolean.compare(dataA.relevant, dataB.relevant)) == 0 &&
           (result = dataA.name.compareTo(dataB.name)) == 0)
            result = Float.compare(dataA.value, dataB.value);
        return result;
    }
}

If you are just looking to sort a collection by a custom order then the following is even cleaner:

myDataList.sort((dataA, dataB) -> {
    int result;
    if((result = Integer.compare(dataA.id, dataB.id)) == 0 &&
       (result = Boolean.compare(dataA.relevant, dataB.relevant)) == 0 &&
       (result = dataA.name.compareTo(dataB.name)) == 0)
        result = Float.compare(dataA.value, dataB.value);
    return result;
});
pallgeuer
  • 1,216
  • 1
  • 7
  • 17
0

Following blog given good chained Comparator example

http://www.codejava.net/java-core/collections/sorting-a-list-by-multiple-attributes-example

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

/**
 * This is a chained comparator that is used to sort a list by multiple
 * attributes by chaining a sequence of comparators of individual fields
 * together.
 *
 */
public class EmployeeChainedComparator implements Comparator<Employee> {

    private List<Comparator<Employee>> listComparators;

    @SafeVarargs
    public EmployeeChainedComparator(Comparator<Employee>... comparators) {
        this.listComparators = Arrays.asList(comparators);
    }

    @Override
    public int compare(Employee emp1, Employee emp2) {
        for (Comparator<Employee> comparator : listComparators) {
            int result = comparator.compare(emp1, emp2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }
}

Calling Comparator:

Collections.sort(listEmployees, new EmployeeChainedComparator(
                new EmployeeJobTitleComparator(),
                new EmployeeAgeComparator(),
                new EmployeeSalaryComparator())
        );
vaquar khan
  • 10,864
  • 5
  • 72
  • 96
0

If you implement the Comparable interface, you'll want to choose one simple property to order by. This is known as natural ordering. Think of it as the default. It's always used when no specific comparator is supplied. Usually this is name, but your use case may call for something different. You are free to use any number of other Comparators you can supply to various collections APIs to override the natural ordering.

Also note that typically if a.compareTo(b) == 0, then a.equals(b) == true. It's ok if not but there are side effects to be aware of. See the excellent javadocs on the Comparable interface and you'll find lots of great information on this.

Mark Renouf
  • 30,697
  • 19
  • 94
  • 123
0

Starting from Steve's answer the ternary operator can be used:

public int compareTo(Person other) {
    int f = firstName.compareTo(other.firstName);
    int l = lastName.compareTo(other.lastName);
    return f != 0 ? f : l != 0 ? l : Integer.compare(age, other.age);
}
Community
  • 1
  • 1
Gerold Broser
  • 14,080
  • 5
  • 48
  • 107
0

It is easy to compare two objects with hashcode method in java`

public class Sample{

  String a=null;
  String b=null;

  public Sample(){
      a="s";
      b="a";
  }
  public Sample(String a,String b){
      this.a=a;
      this.b=b;
  }
  public static void main(String args[]){
      Sample f=new Sample("b","12");
      Sample s=new Sample("b","12");
      //will return true
      System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));

      //will return false
      Sample f=new Sample("b","12");
      Sample s=new Sample("b","13");
      System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));

}
Exodus
  • 57
  • 6
  • 1
    **Please don't do that.** Hashcodes should not be used for equality comparisons but for hashtable indexing. Hash collisions may result in equality for two different objects. Even hashtables rely on *real equality* if hash collisions occur. – Noel Widmer Nov 22 '17 at 13:24
-2

Its easy to do using Google's Guava library.

e.g. Objects.equal(name, name2) && Objects.equal(age, age2) && ...

More examples:

Community
  • 1
  • 1
Andrejs
  • 26,885
  • 12
  • 107
  • 96