Java 如何按多个字段比较对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/369512/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 13:55:00  来源:igfitidea点击:

How to compare objects by multiple fields

javaoop

提问by Yuval Adam

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

您可能会问 a 的姓氏是否在 b 之前,或者 a 是否比 b 年长,等等...

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

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

  • java.lang.Comparableinterface allows comparison by one field only
  • Adding numerous compare methods (i.e. compareByFirstName(), compareByAge(), etc...) is cluttered in my opinion.
  • java.lang.Comparable接口只允许通过一个字段进行比较
  • 在我看来compareByFirstName(),添加大量比较方法(即、compareByAge()等...)是混乱的。

So what is the best way to go about this?

那么最好的方法是什么?

采纳答案by Elie

You can implement a Comparatorwhich compares two Personobjects, 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.

您可以实现Comparator比较两个Person对象的 a,并且您可以根据需要检查任意数量的字段。您可以在比较器中放入一个变量,告诉它要与哪个字段进行比较,尽管编写多个比较器可能会更简单。

回答by Michael Haren

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

对我来说唯一的例外是平等。对于单元测试,覆盖 .Equals(在 .net 中)以确定两个对象之间的几个字段是否相等(而不是引用相等)对我很有用。

回答by Marc Novakowski

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.

您可能只想在 Person 类中定义几种类型的“比较器”子类,而不是比较方法。这样你就可以将它们传递到标准的 Collections 排序方法中。

回答by sblundy

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.

如果用户可以通过多种方式订购人员,您还可以在某处将多个Comparator设置为常量。大多数排序操作和排序集合都将比较器作为参数。

回答by Mark Renouf

If you implement the Comparableinterface, 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.

如果您实现Comparable接口,您将需要选择一个简单的属性进行排序。这被称为自然排序。将其视为默认设置。当没有提供特定的比较器时,它总是被使用。通常这是名称,但您的用例可能需要不同的东西。您可以自由使用任意数量的其他比较器,您可以提供给各种集合 API 来覆盖自然顺序。

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.

另请注意,通常如果 a.compareTo(b) == 0,则 a.equals(b) == true。如果没有也没关系,但有副作用需要注意。请参阅 Comparable 界面上的优秀 javadoc,您会找到很多关于此的重要信息。

回答by Steve Kuo

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 compareTomethod is quite simple:

你应该实现Comparable <Person>. 假设所有字段都不会为空(为了简单起见),那个年龄是一个整数,比较排名是第一,最后,年龄,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);
}

回答by Boune

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

您还可以查看实现 Comparator 的 Enum。

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

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

e.g.

例如

Collections.sort(myChildren, Child.Order.ByAge.descending());

回答by Nigel_V_Thomas

@Patrick To sort more than one field consecutively try ComparatorChain

@Patrick 要连续排序多个字段,请尝试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.

ComparatorChain 是一个按顺序包装一个或多个 Comparator 的 Comparator。ComparatorChain 依次调用每个 Comparator,直到 1) 任何单个 Comparator 返回非零结果(然后返回该结果),或 2)ComparatorChain 耗尽(并返回零)。这种类型的排序与 SQL 中的多列排序非常相似,此类允许 Java 类在对 List 进行排序时模拟这种行为。

为了进一步促进类似 SQL 的排序,列表中任何单个 Comparator 的顺序都可以颠倒。

在调用 compare(Object, Object) 之后调用添加新比较器或更改升序/降序排序的方法将导致 UnsupportedOperationException。但是,请注意不要更改底层的比较器列表或定义排序顺序的 BitSet。

ComparatorChain 的实例不同步。该类在构造时不是线程安全的,但在所有设置操作完成后执行多重比较是线程安全的。

回答by Andrejs

Its easy to do using Google's Guava library.

使用Google 的 Guava library很容易做到。

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

例如 Objects.equal(name, name2) && Objects.equal(age, age2) && ...

More examples:

更多例子:

回答by missingfaktor

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

Comparator为这样的用例手动编写一个IMO 是一个糟糕的解决方案。这种临时方法有许多缺点:

  • No code reuse. Violates DRY.
  • Boilerplate.
  • Increased possibility of errors.
  • 没有代码重用。违反 DRY。
  • 样板。
  • 增加出错的可能性。


So what's the solution?

那么有什么解决办法呢?

First some theory.

先说点理论。

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

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

Now, if Ord Aand 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).

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

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

我们可以将此论证扩展到任意元数,并说:

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

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

Let's call this statement 1.

我们称此语句为 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 Bto A(call that transformation function f), then you can also have Ord B. How? Well, when the two Binstances are to be compared, you first transform them to Ausing fand then apply Ord A.

如果您知道这一点Ord A,并且知道如何转换BA(调用该转换函数f),那么您也可以拥有Ord B. 如何?好吧,当B要比较两个实例时,您首先将它们转换为Ausing f,然后应用Ord A

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

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

Ord A, (B → A)?comapOrd B

Ord A, (B → A)? 地图Ord B

Let's call this statement 2.

我们称这个语句为 2。



Now let's apply this to your example.

现在让我们将其应用到您的示例中。

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

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

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

  • We can easily write a function from Personto (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 comapto get Ord Person.

  • 我们知道Ord String。根据陈述 1, Ord (String, String, String).

  • 我们可以轻松地编写一个函数 from Personto (String, String, String)。(只需返回三个字段。)由于我们知道Ord (String, String, String)and Person → (String, String, String),通过语句 2,我们可以使用comapget Ord Person

QED.

QED。



How do I implement all these concepts?

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

The good news is you don't have to. There already exists a librarywhich 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:

解释:

  • stringOrdis an object of type Ord<String>. This corresponds to our original "supports comparison" proposition.
  • p3Ordis a method that takes Ord<A>, Ord<B>, Ord<C>, and returns Ord<P3<A, B, C>>. This corresponds to statement 1. (P3stands for product with three elements. Product is an algebraic term for composites.)
  • comapcorresponds to well, comap.
  • F<A, B>represents a transformation function A → B.
  • pis a factory method for creating products.
  • The whole expression corresponds to statement 2.
  • stringOrd是一个类型的对象Ord<String>。这对应于我们最初的“支持比较”命题。
  • p3Ord是,需要一个方法Ord<A>Ord<B>Ord<C>,并返回Ord<P3<A, B, C>>。这对应于陈述 1。(P3代表具有三个元素的产品。产品是复合材料的代数术语。)
  • comap对应于,comap
  • F<A, B>表示一个变换函数A → B
  • p是用于创建产品的工厂方法。
  • 整个表达式对应于语句 2。

Hope that helps.

希望有帮助。