How to compare objects by multiple fields

后端 未结 22 2451
暖寄归人
暖寄归人 2020-11-22 00:43

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

public class Person {

    private String firstName;
    private String lastN         


        
相关标签:
22条回答
  • 2020-11-22 01:35

    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()));
    
    }
    
    0 讨论(0)
  • 2020-11-22 01:36

    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);
    }
    
    0 讨论(0)
  • 2020-11-22 01:40

    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).

    0 讨论(0)
  • 2020-11-22 01:41

    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.

    0 讨论(0)
提交回复
热议问题