How to compare objects by multiple fields

后端 未结 22 2557
暖寄归人
暖寄归人 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: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 personOrd = 
     p3Ord(stringOrd, stringOrd, stringOrd).comap(
       new F>() {
         public P3 f(Person x) {
           return p(x.getFirstName(), x.getLastname(), x.getAge());
         }
       }
     );
    

    Explanation:

    Hope that helps.

提交回复
热议问题