在 Java 中检查两个对象是否完全相等
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33104205/
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
Check if two objects are completely equal in Java
提问by Guy
I've got a Java class, here's an example:
我有一个 Java 类,这是一个例子:
public class Car {
private int fuelType;
private Date made;
private String name;
.
.
. // and so on
Now let's say I have two car objects and I want to compare if all their variables are equal.
现在假设我有两个汽车对象,我想比较它们的所有变量是否相等。
Right now, I've solved this by overriding method equals(Object o)
and I check if all the variables match in both objects.
现在,我已经通过覆盖方法解决了这个问题,equals(Object o)
并检查了两个对象中的所有变量是否匹配。
The problem here is that if I have 20 classes, I'll have to override equals(Object o)
in every single one of them.
这里的问题是,如果我有 20 个类,我将不得不覆盖equals(Object o)
其中的每一个。
Is there a way create some sort of universal method that could compare any of the two objects that I pass to it and let me know if they match in every single variable or not?
有没有办法创建某种通用方法,可以比较我传递给它的两个对象中的任何一个,并让我知道它们是否在每个变量中匹配?
回答by Michael Lloyd Lee mlk
You have a few options for automating Equals & Hashcode (option #3 BLEW MY MIND!):
您有几个选项可以自动执行 Equals & Hashcode(选项 #3 BLEW MY MIND!):
- Your IDE. I would not recommend it for most objects as they can slowly drift out of date with the actual class definition. They also look ugly and pollute your codebase with boilerplate code.
- Apache Commonshas a bunch of stuff for making this easier, including a reflective versionso no risk of drifting out of date with the class definition. It is better than #1 unless you require a speedy equals/hashcode, but still too much boilerplate for my liking.
- Project Lombokand annotation processing. Whack an
EqualsAndHashCode
annotation on ya class and be done with it. I recommend using Project Lombok. It adds a touch of magic into the build (but not much) and so requires a plugin for your IDE to behave nicely but they are a small price to pay for no boilerplate code. Lombok is an annotation processor that run at compile time so you have no runtime performance hit. - Using a different language that supports it out the box, but also targets the JVM. Groovyuses an annotationand Kotlinsupports data classes. Unless your existing code can quickly be converted, I would avoid this.
- Google's Autohas an AutoValue. Like Project Lombok this is an annotation processor, however has less magic at the expense of little more boilerplate (thanks to Louis Wasserman)
- 你骑。我不会为大多数对象推荐它,因为它们会随着实际的类定义慢慢过时。它们看起来也很丑陋,并且会用样板代码污染您的代码库。
- Apache Commons有很多东西可以使这变得更容易,包括反射版本,因此没有与类定义过时的风险。除非您需要快速的 equals/hashcode,否则它比 #1 好,但我喜欢的样板仍然太多。
- 龙目岛项目和注释处理。重击的
EqualsAndHashCode
雅类注释,并用它做。我建议使用 Project Lombok。它为构建添加了一点魔力(但不是很多),因此需要一个插件来让您的 IDE 表现良好,但对于没有样板代码,它们是一个很小的代价。Lombok 是一个在编译时运行的注释处理器,因此您不会影响运行时性能。 - 使用一种不同的语言来支持它,但也以 JVM 为目标。Groovy使用注解,而Kotlin支持数据类。除非您现有的代码可以快速转换,否则我会避免这种情况。
- 谷歌的 Auto有一个AutoValue。像 Project Lombok 一样,这是一个注释处理器,但是以更少的样板为代价,它的魔力更少(感谢Louis Wasserman)
回答by hic1086
回答by Voo
I'll take the dissenting opinion to the majority (use apache commons with reflection) here: Yes, this is a bit code you have to write (let your IDE generate really), but you only have to do it once and the number of data classes that need to implement equals/hashcode is generally rather manageable - at least in all of the large projects (250k+ LOC) I worked on.
我将在此处向大多数人提出反对意见(使用 apache commons 和反射):是的,这是您必须编写的一些代码(让您的 IDE 真正生成),但您只需要执行一次并且数量需要实现 equals/hashcode 的数据类通常相当易于管理 - 至少在我参与的所有大型项目 (250k+ LOC) 中。
Sure if you add a new member to the class you will have to remember to update the equals/hashcode functions, but that's generally easy to notice, at the latest during code reviews.
当然,如果您向类添加新成员,您将必须记住更新 equals/hashcode 函数,但这通常很容易注意到,最迟在代码期间。
And honestly if you use a simple little helper class that's even in Java7, you can cut down the code that Wana Ant showed immensely. Really all you need is:
老实说,如果您使用一个简单的小助手类,即使是在 Java7 中,您也可以极大地减少 Wana Ant 展示的代码。你真正需要的是:
@Override
public boolean equals(Object o) {
if (o instanceof Car) { // nb: broken if car is not final - other topic
Car other = (Car) o;
return Objects.equals(fuelType, other.fuelType) &&
Objects.equals(made, other.made) &&
Objects.equals(name, other.name);
}
return false;
}
similar for hashcode:
类似于哈希码:
@Override
public int hashCode() {
return Objects.hash(fuelType, made, name);
}
Not as short as the reflection solution? True, but it's simple, easy to maintain, adapt and read - and performance is orders of magnitude better (which for classes that implement equals and hashcode is often important)
不像反射解决方案那么短?是的,但它简单、易于维护、适应和阅读 - 并且性能要好几个数量级(这对于实现 equals 和 hashcode 的类通常很重要)
回答by zdenda.online
Typically you can generate equals/hashCode methods by your IDE - all big players in this field are capable of that (Eclipse, IntelliJ Idea and Netbeans).
通常,您可以通过 IDE 生成 equals/hashCode 方法——该领域的所有大玩家都能够做到这一点(Eclipse、IntelliJ Idea 和 Netbeans)。
Generally you can create some code that will use reflection but I don't recommend this one as objective approach is clearer and more maintainable. Also reflection won't be as fast as "standard" way. If you really want to go this way, there exist utilities like EqualsBuilderand HashCodeBuilder.
通常,您可以创建一些使用反射的代码,但我不推荐使用这种方法,因为客观方法更清晰且更易于维护。此外,反射不会像“标准”方式那样快。如果你真的想走这条路,有像EqualsBuilder和HashCodeBuilder这样的实用程序。
Just for your information, there are JVM-based languages that already support these features, e.g. Kotlin data classes, which can be pretty nicely used in existing Java projects.
仅供参考,有一些基于 JVM 的语言已经支持这些特性,例如 Kotlin数据类,它们可以很好地用于现有的 Java 项目。
回答by Daniel Pryden
I'll just throw in a plug for my favorite solution to this problem: @AutoValue
.
我会为我最喜欢的这个问题的解决方案插入一个插件:@AutoValue
.
This is an open-source project from Google that provides an annotation processor that generates a synthetic class that implements equals
and hashCode
for you.
这是谷歌从一个开源项目,提供了生成合成类注释处理器实现equals
和hashCode
为您服务。
Since it's auto-generated code, you don't have to worry about accidentally forgetting a field or messing up the equals
or hashCode
implementation. But since the code is generated at compile time, there's zero runtime overhead (unlike reflection-based solutions). It's also "API-invisible" -- users of your class can't tell the difference between an @AutoValue
type and a type you implemented yourself, and you can change back and forth in the future without breaking callers.
由于它是自动生成的代码,因此您不必担心意外忘记某个字段或弄乱equals
orhashCode
实现。但由于代码是在编译时生成的,因此运行时开销为零(与基于反射的解决方案不同)。它也是“API 不可见的”——您的类的用户无法区分@AutoValue
类型和您自己实现的类型,并且您可以在将来来回更改而不会中断调用者。
See also this presentationwhich explains the rationale and does a better job comparing it to other approaches.
另请参阅此演示文稿,它解释了基本原理,并将其与其他方法相比做得更好。
回答by user902383
Theoretically you could use reflection to create some kind of util, as many people suggest you in comments. Personally I don't recommend you to do it. you will end up with something which is partially working.
从理论上讲,您可以使用反射来创建某种实用程序,正如许多人在评论中建议的那样。我个人不建议你这样做。你最终会得到一些部分有效的东西。
Many things in Java rely on equal
or hashCode
, for example method contains
which you can find in anything which implements Collection
.
Java 中的许多东西都依赖于equal
or hashCode
,例如contains
您可以在任何实现Collection
.
Overriding equal
(and hashCode
) is recommended solution. By addition, i think any decent IDE will have option to generate them for you. Hence you can do it quicker than by using reflection.
推荐的解决方案是覆盖equal
(和hashCode
)。此外,我认为任何体面的 IDE 都可以选择为您生成它们。因此,您可以比使用反射更快地做到这一点。
回答by Andrei Toader
That's the way I would do it:
这就是我会这样做的方式:
@Override
public boolean equals(Object obj) {
if (obj instanceof Car) {
return internalEquals((Car) obj);
}
return super.equals(obj);
}
protected boolean internalEquals(Car other) {
if(this==other){
return true;
}
if (other != null) {
//suppose fuelType can be Integer.
if (this.getFuelType() !=null) {
if (other.getFuelType() == null) {
return false;
} else if (!this.getFuelType().equals(other.getFuelType())) {
return false;
}
} else if(other.getFuelType()!=null){
return false;
}
if (this.getName() != null) {
if (other.getName() == null) {
return false;
} else if (!this.getName().equals(other.getName())) {
return false;
}
}
else if(other.getName()!=null){
return false;
}
if (this.getDate() != null) {
if (other.getDate() == null) {
return false;
} else if (!this.getDate().getTime()!=(other.getDate().getTime())) {
return false;
}
}
else if(other.getDate()!=null){
return false;
}
return true;
} else {
return false;
}
}
EDIT
Simplified version
编辑
简化版
public class Utils{
/**
* Compares the two given objects and returns true,
* if they are equal and false, if they are not.
* @param a one of the two objects to compare
* @param b the other one of the two objects to compare
* @return if the two given lists are equal.
*/
public static boolean areObjectsEqual(Object a, Object b) {
if (a == b){
return true;
}
return (a!=null && a.equals(b));
}
public static boolean areDatesEqual(Date a, Date b){
if(a == b){
return true;
}
if(a==null || b==null){
return false;
}
return a.getTime() == b.getTime();
}
}
@Override
public boolean equals(other obj) {
if(this == other){
return true;
}
if(other == null){
return false;
}
if (other instanceof Car) {
return internalEquals((Car) other);
}
return super.equals(obj);
}
protected boolean internalEquals(Car other) {
//suppose fuelType can be Integer.
if (!Utils.areObjectsEqual(this.getName(), other.getName()){
return false;
}
if (!Utils.areObjectsEqual(this.getName(), other.getName()){
return false;
}
if (!Utils.areDatesEqual(this.getDate(), other.getDate()){
return false;
}
return true;
}
}
Also don't forget about hashcode, they code hand in hand.
也不要忘记哈希码,它们是携手编码的。