假设你有一些对象,它们有几个字段可以比较:

public class Person {

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

    /* Constructors */

    /* Methods */

}

所以在这个例子中,当你问if:

a.compareTo(b) > 0

你可能会问a的姓是不是在b的姓之前,或者a的年龄是不是比b大,等等……

在不增加不必要的混乱或开销的情况下,在这些类型的对象之间进行多重比较的最干净的方法是什么?

comparable接口只允许通过一个字段进行比较 在我看来,添加大量的比较方法(如compareByFirstName(), compareByAge()等)是混乱的。

那么最好的解决办法是什么呢?


当前回答

另一个可以考虑的选项是Apache Commons。它提供了很多选项。

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();
}

其他回答

在我看来,为这样的用例手动编写Comparator是一个糟糕的解决方案。这种特别的方法有很多缺点:

没有代码重用。违反了干。 样板。 增加了出错的可能性。


那么解决方案是什么呢?

首先是一些理论。

让我们用Ord A来表示命题“type A支持比较”(从程序的角度来看,您可以将Ord A看作一个包含比较两个A的逻辑的对象。是的,就像Comparator一样。)

现在,如果Ord A和Ord B,那么它们的合成(A, B)也应该支持比较。例如,Ord (A, B)。如果Ord A, Ord B,和Ord C,那么Ord (A, B, C)。

我们可以将这个论证扩展到任意性,并说:

A站,B站,C站,……, Ord Z⇒Ord (A, B, C, .., Z)

我们称这个为表述一。

复合材料的比较将像您在问题中描述的那样工作:首先尝试第一个比较,然后是下一个比较,然后是下一个比较,依此类推。

这是解的第一部分。现在是第二部分。

如果你知道A字词,也知道如何将B转换为A(称之为转换函数f),那么你也可以有B字词,怎么做?当要比较两个B实例时,首先使用f将它们转换为A,然后应用Ord A。

在这里,我们将变换B→A映射到Ord A→Ord B,这被称为逆变映射(或简称comap)。

Ord A, (B→A)

我们称这个为表述二。


现在让我们把这个应用到你的例子中。

您有一个名为Person的数据类型,它包含三个String类型的字段。

我们知道Ord String。通过语句1,Ord(字符串,字符串,字符串)。 我们可以很容易地编写一个从Person到(String, String, String)的函数。(只返回三个字段。)由于我们知道Ord (String, String, String)和Person→(String, String, String),通过语句2,我们可以使用comap来获得Ord Person。

QED.


我如何实现所有这些概念?

好消息是你不必这么做。已经有一个库实现了这篇文章中描述的所有想法。(如果你对这些是如何实现的感到好奇,你可以深入了解一下。)

下面是代码的外观:

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());
     }
   }
 );

解释:

stringOrd是Ord<String>类型的对象。这与我们最初的“支持比较”命题相对应。 p3Ord是一个获取Ord< a >, Ord<B>, Ord<C>,并返回Ord<P3< a, B, C>>的方法。这对应于表述一。(P3代表三要素产品。Product是复合材料的代数术语。) Comap对应于Comap。 F<A, B>表示变换函数A→B。 P是创建产品的工厂方法。 整个表达式对应于表述2。

希望这能有所帮助。

另一个可以考虑的选项是Apache Commons。它提供了很多选项。

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();
}

您应该实现Comparable <Person>。假设所有字段都不为空(为了简单起见),年龄是int型,比较排名是第一,最后,年龄,compareTo方法非常简单:

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);
}

如果我们必须基于多个字段对Person对象进行排序,则相同的代码实现在这里。

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);
    }
}

}

在java中使用hashcode方法比较两个对象很容易。

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()));

}