I'm trying to write unit tests for a variety of clone()
operations inside a large project and I'm wondering if there is an existing class somewhere that is capable of taking two objects of the same type, doing a deep comparison, and saying if they're identical or not?
我正在尝试为大型项目中的各种clone()操作编写单元测试,我想知道是否存在一个能够获取相同类型的两个对象的现有类,进行深入比较,以及说他们是否相同?
14 个解决方案
#1
58
Unitils has this functionality:
Unitils具有此功能:
Equality assertion through reflection, with different options like ignoring Java default/null values and ignoring order of collections
通过反射进行平等断言,使用不同的选项,例如忽略Java默认/空值并忽略集合的顺序
#2
27
I love this question! Mainly because it is hardly ever answered or answered badly. It's like nobody has figured it out yet. Virgin territory :)
我喜欢这个问题!主要是因为它几乎没有得到很好的回答或回答。这就像没有人想出来的那样。处女地区:)
First off, don't even think about using equals
. The contract of equals
, as defined in the javadoc, is an equivalence relation (reflexive, symmetric, and transitive), not an equality relation. For that, it would also have to be antisymmetric. The only implementation of equals
that is (or ever could be) a true equality relation is the one in java.lang.Object
. Even if you did use equals
to compare everything in the graph, the risk of breaking the contract is quite high. As Josh Bloch pointed out in Effective Java, the contract of equals is very easy to break:
首先,甚至不要考虑使用equals。在javadoc中定义的equals的契约是等价关系(自反,对称和传递),而不是平等关系。为此,它也必须是反对称的。唯一的equals实现(或者可能是)真正的相等关系是java.lang.Object中的实现。即使您确实使用等于比较图表中的所有内容,违约的风险也非常高。正如Josh Bloch在Effective Java中指出的那样,平等契约很容易破解:
"There is simply no way to extend an instantiable class and add an aspect while preserving the equals contract"
Besides what good does a boolean method really do you anyway? It'd be nice to actually encapsulate all the differences between the original and the clone, don't you think? Also, I'll assume here that you don't want to be bothered with writing/maintaining comparison code for each object in the graph, but rather you're looking for something that will scale with the source as it changes over time.
除了布尔方法有什么好处之外你呢?真的很好地封装了原始和克隆之间的所有差异,你不觉得吗?另外,我在这里假设您不想为图中的每个对象编写/维护比较代码而烦恼,而是在寻找随着时间的推移而随源变化的内容。
Soooo, what you really want is some kind of state comparison tool. How that tool is implemented is really dependent on the nature of your domain model and your performance restrictions. In my experience, there is no generic magic bullet. And it will be slow over a large number of iterations. But for testing the completeness of a clone operation, it'll do the job pretty well. Your two best options are serialization and reflection.
Soooo,你真正想要的是某种状态比较工具。该工具的实现方式实际上取决于域模型的性质和性能限制。根据我的经验,没有通用的魔术子弹。而且在大量迭代中它会很慢。但是为了测试克隆操作的完整性,它将很好地完成工作。您最好的两个选项是序列化和反射。
Some issues you will encounter:
您将遇到的一些问题:
- Collection order: Should two collections be considered similar if they hold the same objects, but in a different order?
- 收集顺序:如果两个集合拥有相同的对象,但它们的顺序不同,是否应该被认为是相似的?
- Which fields to ignore: Transient? Static?
- 哪些字段要忽略:瞬态?静态的?
- Type equivalence: Should field values be of exactly the same type? Or is it ok for one to extend the other?
- 类型等价:字段值应该是完全相同的类型吗?或者是否可以扩展另一个?
- There's more, but I forget...
- 还有更多,但我忘了......
XStream is pretty fast and combined with XMLUnit will do the job in just a few lines of code. XMLUnit is nice because it can report all the differences, or just stop at the first one it finds. And its output includes the xpath to the differing nodes, which is nice. By default it doesn't allow unordered collections, but it can be configured to do so. Injecting a special difference handler (Called a DifferenceListener
) allows you to specify the way you want to deal with differences, including ignoring order. However, as soon as you want to do anything beyond the simplest customization, it becomes difficult to write and the details tend to be tied down to a specific domain object.
XStream非常快,与XMLUnit相结合只需几行代码即可完成。 XMLUnit很不错,因为它可以报告所有差异,或者只是停留在它找到的第一个差异上。它的输出包括不同节点的xpath,这很好。默认情况下,它不允许无序集合,但可以配置为执行此操作。注入特殊差异处理程序(Called a DifferenceListener)允许您指定处理差异的方式,包括忽略顺序。但是,只要您想要执行除最简单的自定义之外的任何操作,就会很难编写,并且细节往往会绑定到特定的域对象。
My personal preference is to use reflection to cycle through all the declared fields and drill down into each one, tracking differences as I go. Word of warning: Don't use recursion unless you like stack overflow exceptions. Keep things in scope with a stack (use a LinkedList
or something). I usually ignore transient and static fields, and I skip object pairs that I've already compared, so I don't end up in infinite loops if someone decided to write self-referential code (However, I always compare primitive wrappers no matter what, since the same object refs are often reused). You can configure things up front to ignore collection ordering and to ignore special types or fields, but I like to define my state comparison policies on the fields themselves via annotations. This, IMHO, is exactly what annotations were meant for, to make meta data about the class available at runtime. Something like:
我个人的偏好是使用反射循环遍历所有声明的字段并向下钻取每个字段,跟踪差异。警告:除非您喜欢堆栈溢出异常,否则不要使用递归。使用堆栈保存范围(使用LinkedList或其他东西)。我通常忽略瞬态和静态字段,并且我跳过我已经比较的对象对,所以如果有人决定编写自引用代码,我不会在无限循环中结束(但是,我总是比较原始包装器,不管是什么,因为相同的对象引用经常被重用)。您可以预先配置以忽略集合排序并忽略特殊类型或字段,但我喜欢通过注释在字段本身上定义状态比较策略。恕我直言,这正是注释的意义,使运行时可以获得有关类的元数据。就像是:
@StatePolicy(unordered=true, ignore=false, exactTypesOnly=true)
private List<StringyThing> _mylist;
I think this is actually a really hard problem, but totally solvable! And once you have something that works for you, it is really, really, handy :)
我认为这实际上是一个非常难的问题,但完全可以解决!一旦你有一些适合你的东西,它真的非常方便:)
So, good luck. And if you come up with something that's just pure genius, don't forget to share!
祝你好运。如果你想出的东西只是纯粹的天才,别忘了分享!
#3
12
See DeepEquals and DeepHashCode() within java-util: https://github.com/jdereg/java-util
请参阅java-util中的DeepEquals和DeepHashCode():https://github.com/jdereg/java-util
This class does exactly what the original author requests.
这个类完全符合原作者的要求。
#4
6
I am usin XStream:
我在XStream中使用:
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object o) {
XStream xstream = new XStream();
String oxml = xstream.toXML(o);
String myxml = xstream.toXML(this);
return myxml.equals(oxml);
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
XStream xstream = new XStream();
String myxml = xstream.toXML(this);
return myxml.hashCode();
}
#5
6
Just had to implement comparison of two entity instances revised by Hibernate Envers. I started writing my own differ but then found the following framework.
只需要实现Hibernate Envers修改的两个实体实例的比较。我开始写自己的不同,但后来找到了以下框架。
https://github.com/SQiShER/java-object-diff
https://github.com/SQiShER/java-object-diff
You can compare two objects of the same type and it will show changes, additions and removals. If there are no changes, then the objects are equal (in theory). Annotations are provided for getters that should be ignored during the check. The frame work has far wider applications than equality checking, i.e. I am using to generate a change-log.
您可以比较两个相同类型的对象,它将显示更改,添加和删除。如果没有变化,则对象相等(理论上)。为检查期间应忽略的getter提供注释。框架工作具有比等式检查更广泛的应用程序,即我用于生成更改日志。
Its performance is OK, when comparing JPA entities, be sure to detach them from the entity manager first.
它的性能还可以,比较JPA实体时,请务必先将它们从实体管理器中分离出来。
#6
3
http://www.unitils.org/tutorial-reflectionassert.html
http://www.unitils.org/tutorial-reflectionassert.html
public class User {
private long id;
private String first;
private String last;
public User(long id, String first, String last) {
this.id = id;
this.first = first;
this.last = last;
}
}
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);
#7
2
If your objects implement Serializable you can use this:
如果您的对象实现Serializable,您可以使用:
public static boolean deepCompare(Object o1, Object o2) {
try {
ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
ObjectOutputStream oos1 = new ObjectOutputStream(baos1);
oos1.writeObject(o1);
oos1.close();
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
ObjectOutputStream oos2 = new ObjectOutputStream(baos2);
oos2.writeObject(o2);
oos2.close();
return Arrays.equals(baos1.toByteArray(), baos2.toByteArray());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#8
2
Override The equals() Method
You can simply override the equals() method of the class using the EqualsBuilder.reflectionEquals() as explained here:
您可以使用EqualsBuilder.reflectionEquals()简单地覆盖类的equals()方法,如下所述:
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
#9
1
Your Linked List example is not that difficult to handle. As the code traverses the two object graphs, it places visited objects in a Set or Map. Before traversing into another object reference, this set is tested to see if the object has already been traversed. If so, no need to go further.
您的链接列表示例并不难处理。当代码遍历两个对象图时,它将访问的对象放在Set或Map中。在遍历另一个对象引用之前,将测试此集以查看是否已遍历该对象。如果是这样,就没有必要再往前走了。
I agree with the person above who said use a LinkedList (like a Stack but without synchronized methods on it, so it is faster). Traversing the object graph using a Stack, while using reflection to get each field, is the ideal solution. Written once, this "external" equals() and "external" hashCode() is what all equals() and hashCode() methods should call. Never again do you need a customer equals() method.
我同意上面说过使用LinkedList的人(比如Stack但没有使用synchronized方法,所以它更快)。使用堆栈遍历对象图,同时使用反射来获取每个字段,是理想的解决方案。写一次,这个“外部”等于()和“外部”hashCode()是所有equals()和hashCode()方法应该调用的。您再也不需要客户equals()方法了。
I wrote a bit of code that traverses a complete object graph, listed over at Google Code. See json-io (http://code.google.com/p/json-io/). It serializes a Java object graph into JSON and deserialized from it. It handles all Java objects, with or without public constructors, Serializeable or not Serializable, etc. This same traversal code will be the basis for the external "equals()" and external "hashcode()" implementation. Btw, the JsonReader / JsonWriter (json-io) is usually faster than the built-in ObjectInputStream / ObjectOutputStream.
我编写了一些代码,遍历完整的对象图,列在Google Code上。请参阅json-io(http://code.google.com/p/json-io/)。它将Java对象图序列化为JSON并从中反序列化。它处理所有Java对象,有或没有公共构造函数,Serializeable或Serializable等。这个相同的遍历代码将是外部“equals()”和外部“hashcode()”实现的基础。顺便说一下,JsonReader / JsonWriter(json-io)通常比内置的ObjectInputStream / ObjectOutputStream更快。
This JsonReader / JsonWriter could be used for comparison, but it will not help with hashcode. If you want a universal hashcode() and equals(), it needs it's own code. I may be able to pull this off with a generic graph visitor. We'll see.
这个JsonReader / JsonWriter可以用于比较,但它对hashcode没有帮助。如果你想要一个通用的hashcode()和equals(),它需要它自己的代码。我可以用通用图形访问者来解决这个问题。我们拭目以待。
Other considerations - static fields - that's easy - they can be skipped because all equals() instances would have the same value for static fields, as the static fields is shared across all instances.
其他注意事项 - 静态字段 - 这很容易 - 可以跳过它们,因为所有equals()实例对静态字段都具有相同的值,因为静态字段在所有实例之间共享。
As for transient fields - that will be a selectable option. Sometimes you may want transients to count other times not. "Sometimes you feel like a nut, sometimes you don't."
对于瞬态字段 - 这将是一个可选择的选项。有时您可能希望瞬态计数不是其他时间。 “有时候你感觉自己像个坚果,有时你却没有。”
Check back to the json-io project (for my other projects) and you will find the external equals() / hashcode() project. I don't have a name for it yet, but it will be obvious.
回到json-io项目(对于我的其他项目),你会发现外部的equals()/ hashcode()项目。我还没有它的名字,但很明显。
#10
1
Apache gives you something, convert both objects to string and compare strings, but you have to Override toString()
Apache为您提供了一些东西,将两个对象转换为字符串并比较字符串,但您必须覆盖toString()
obj1.toString().equals(obj2.toString())
Override toString()
覆盖toString()
If all fields are primitive types :
如果所有字段都是原始类型:
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@Override
public String toString() {return
ReflectionToStringBuilder.toString(this);}
If you have non primitive fields and/or collection and/or map :
如果您有非原始字段和/或集合和/或映射:
// Within class
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@Override
public String toString() {return
ReflectionToStringBuilder.toString(this,new
MultipleRecursiveToStringStyle());}
// New class extended from Apache ToStringStyle
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.*;
public class MultipleRecursiveToStringStyle extends ToStringStyle {
private static final int INFINITE_DEPTH = -1;
private int maxDepth;
private int depth;
public MultipleRecursiveToStringStyle() {
this(INFINITE_DEPTH);
}
public MultipleRecursiveToStringStyle(int maxDepth) {
setUseShortClassName(true);
setUseIdentityHashCode(false);
this.maxDepth = maxDepth;
}
@Override
protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
if (value.getClass().getName().startsWith("java.lang.")
|| (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
buffer.append(value);
} else {
depth++;
buffer.append(ReflectionToStringBuilder.toString(value, this));
depth--;
}
}
@Override
protected void appendDetail(StringBuffer buffer, String fieldName,
Collection<?> coll) {
for(Object value: coll){
if (value.getClass().getName().startsWith("java.lang.")
|| (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
buffer.append(value);
} else {
depth++;
buffer.append(ReflectionToStringBuilder.toString(value, this));
depth--;
}
}
}
@Override
protected void appendDetail(StringBuffer buffer, String fieldName, Map<?, ?> map) {
for(Map.Entry<?,?> kvEntry: map.entrySet()){
Object value = kvEntry.getKey();
if (value.getClass().getName().startsWith("java.lang.")
|| (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
buffer.append(value);
} else {
depth++;
buffer.append(ReflectionToStringBuilder.toString(value, this));
depth--;
}
value = kvEntry.getValue();
if (value.getClass().getName().startsWith("java.lang.")
|| (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
buffer.append(value);
} else {
depth++;
buffer.append(ReflectionToStringBuilder.toString(value, this));
depth--;
}
}
}}
#11
0
I guess you know this, but In theory, you're supposed to always override .equals to assert that two objects are truly equal. This would imply that they check the overridden .equals methods on their members.
我想你知道这一点,但理论上,你应该总是覆盖.equals断言两个对象是真正相等的。这意味着他们会检查其成员上被覆盖的.equals方法。
This kind of thing is why .equals is defined in Object.
这就是为什么.equals在Object中定义的原因。
If this were done consistently you wouldn't have a problem.
如果一直这样做,你就不会有问题。
#12
0
Hamcrest has the Matcher samePropertyValuesAs. But it relies on the JavaBeans Convention (uses getters and setters). Should the objects that are to be compared not have getters and setters for their attributes, this will not work.
Hamcrest具有Matcher samePropertyValuesAs。但它依赖于JavaBeans公约(使用getter和setter)。如果要比较的对象不具有其属性的getter和setter,则这将不起作用。
import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
import static org.junit.Assert.assertThat;
import org.junit.Test;
public class UserTest {
@Test
public void asfd() {
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertThat(user1, samePropertyValuesAs(user2)); // all good
user2 = new User(1, "John", "Do");
assertThat(user1, samePropertyValuesAs(user2)); // will fail
}
}
The user bean - with getters and setters
用户bean - 带有getter和setter
public class User {
private long id;
private String first;
private String last;
public User(long id, String first, String last) {
this.id = id;
this.first = first;
this.last = last;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
}
#13
0
A halting guarantee for such a deep comparison might be a problem. What should the following do? (If you implement such a comparator, this would make a good unit test.)
这种深度比较的暂停保证可能是一个问题。以下应该怎么做? (如果你实现这样的比较器,这将是一个很好的单元测试。)
LinkedListNode a = new LinkedListNode();
a.next = a;
LinkedListNode b = new LinkedListNode();
b.next = b;
System.out.println(DeepCompare(a, b));
Here's another:
这是另一个:
LinkedListNode c = new LinkedListNode();
LinkedListNode d = new LinkedListNode();
c.next = d;
d.next = c;
System.out.println(DeepCompare(c, d));
#14
0
I think the easiest solution inspired by Ray Hulha solution is to serialize the object and then deep compare the raw result.
我认为Ray Hulha解决方案启发的最简单的解决方案是序列化对象,然后深入比较原始结果。
The serialization could be either byte, json, xml or simple toString etc. ToString seems to be cheaper. Lombok generates free easy customizable ToSTring for us. See example below.
序列化可以是字节,json,xml或简单的toString等.ToString似乎更便宜。 Lombok为我们生成免费的易于定制的ToSTring。见下面的例子。
@ToString @Getter @Setter
class foo{
boolean foo1;
String foo2;
public boolean deepCompare(Object other) { //for cohesiveness
return other != null && this.toString().equals(other.toString());
}
}
#1
58
Unitils has this functionality:
Unitils具有此功能:
Equality assertion through reflection, with different options like ignoring Java default/null values and ignoring order of collections
通过反射进行平等断言,使用不同的选项,例如忽略Java默认/空值并忽略集合的顺序
#2
27
I love this question! Mainly because it is hardly ever answered or answered badly. It's like nobody has figured it out yet. Virgin territory :)
我喜欢这个问题!主要是因为它几乎没有得到很好的回答或回答。这就像没有人想出来的那样。处女地区:)
First off, don't even think about using equals
. The contract of equals
, as defined in the javadoc, is an equivalence relation (reflexive, symmetric, and transitive), not an equality relation. For that, it would also have to be antisymmetric. The only implementation of equals
that is (or ever could be) a true equality relation is the one in java.lang.Object
. Even if you did use equals
to compare everything in the graph, the risk of breaking the contract is quite high. As Josh Bloch pointed out in Effective Java, the contract of equals is very easy to break:
首先,甚至不要考虑使用equals。在javadoc中定义的equals的契约是等价关系(自反,对称和传递),而不是平等关系。为此,它也必须是反对称的。唯一的equals实现(或者可能是)真正的相等关系是java.lang.Object中的实现。即使您确实使用等于比较图表中的所有内容,违约的风险也非常高。正如Josh Bloch在Effective Java中指出的那样,平等契约很容易破解:
"There is simply no way to extend an instantiable class and add an aspect while preserving the equals contract"
Besides what good does a boolean method really do you anyway? It'd be nice to actually encapsulate all the differences between the original and the clone, don't you think? Also, I'll assume here that you don't want to be bothered with writing/maintaining comparison code for each object in the graph, but rather you're looking for something that will scale with the source as it changes over time.
除了布尔方法有什么好处之外你呢?真的很好地封装了原始和克隆之间的所有差异,你不觉得吗?另外,我在这里假设您不想为图中的每个对象编写/维护比较代码而烦恼,而是在寻找随着时间的推移而随源变化的内容。
Soooo, what you really want is some kind of state comparison tool. How that tool is implemented is really dependent on the nature of your domain model and your performance restrictions. In my experience, there is no generic magic bullet. And it will be slow over a large number of iterations. But for testing the completeness of a clone operation, it'll do the job pretty well. Your two best options are serialization and reflection.
Soooo,你真正想要的是某种状态比较工具。该工具的实现方式实际上取决于域模型的性质和性能限制。根据我的经验,没有通用的魔术子弹。而且在大量迭代中它会很慢。但是为了测试克隆操作的完整性,它将很好地完成工作。您最好的两个选项是序列化和反射。
Some issues you will encounter:
您将遇到的一些问题:
- Collection order: Should two collections be considered similar if they hold the same objects, but in a different order?
- 收集顺序:如果两个集合拥有相同的对象,但它们的顺序不同,是否应该被认为是相似的?
- Which fields to ignore: Transient? Static?
- 哪些字段要忽略:瞬态?静态的?
- Type equivalence: Should field values be of exactly the same type? Or is it ok for one to extend the other?
- 类型等价:字段值应该是完全相同的类型吗?或者是否可以扩展另一个?
- There's more, but I forget...
- 还有更多,但我忘了......
XStream is pretty fast and combined with XMLUnit will do the job in just a few lines of code. XMLUnit is nice because it can report all the differences, or just stop at the first one it finds. And its output includes the xpath to the differing nodes, which is nice. By default it doesn't allow unordered collections, but it can be configured to do so. Injecting a special difference handler (Called a DifferenceListener
) allows you to specify the way you want to deal with differences, including ignoring order. However, as soon as you want to do anything beyond the simplest customization, it becomes difficult to write and the details tend to be tied down to a specific domain object.
XStream非常快,与XMLUnit相结合只需几行代码即可完成。 XMLUnit很不错,因为它可以报告所有差异,或者只是停留在它找到的第一个差异上。它的输出包括不同节点的xpath,这很好。默认情况下,它不允许无序集合,但可以配置为执行此操作。注入特殊差异处理程序(Called a DifferenceListener)允许您指定处理差异的方式,包括忽略顺序。但是,只要您想要执行除最简单的自定义之外的任何操作,就会很难编写,并且细节往往会绑定到特定的域对象。
My personal preference is to use reflection to cycle through all the declared fields and drill down into each one, tracking differences as I go. Word of warning: Don't use recursion unless you like stack overflow exceptions. Keep things in scope with a stack (use a LinkedList
or something). I usually ignore transient and static fields, and I skip object pairs that I've already compared, so I don't end up in infinite loops if someone decided to write self-referential code (However, I always compare primitive wrappers no matter what, since the same object refs are often reused). You can configure things up front to ignore collection ordering and to ignore special types or fields, but I like to define my state comparison policies on the fields themselves via annotations. This, IMHO, is exactly what annotations were meant for, to make meta data about the class available at runtime. Something like:
我个人的偏好是使用反射循环遍历所有声明的字段并向下钻取每个字段,跟踪差异。警告:除非您喜欢堆栈溢出异常,否则不要使用递归。使用堆栈保存范围(使用LinkedList或其他东西)。我通常忽略瞬态和静态字段,并且我跳过我已经比较的对象对,所以如果有人决定编写自引用代码,我不会在无限循环中结束(但是,我总是比较原始包装器,不管是什么,因为相同的对象引用经常被重用)。您可以预先配置以忽略集合排序并忽略特殊类型或字段,但我喜欢通过注释在字段本身上定义状态比较策略。恕我直言,这正是注释的意义,使运行时可以获得有关类的元数据。就像是:
@StatePolicy(unordered=true, ignore=false, exactTypesOnly=true)
private List<StringyThing> _mylist;
I think this is actually a really hard problem, but totally solvable! And once you have something that works for you, it is really, really, handy :)
我认为这实际上是一个非常难的问题,但完全可以解决!一旦你有一些适合你的东西,它真的非常方便:)
So, good luck. And if you come up with something that's just pure genius, don't forget to share!
祝你好运。如果你想出的东西只是纯粹的天才,别忘了分享!
#3
12
See DeepEquals and DeepHashCode() within java-util: https://github.com/jdereg/java-util
请参阅java-util中的DeepEquals和DeepHashCode():https://github.com/jdereg/java-util
This class does exactly what the original author requests.
这个类完全符合原作者的要求。
#4
6
I am usin XStream:
我在XStream中使用:
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object o) {
XStream xstream = new XStream();
String oxml = xstream.toXML(o);
String myxml = xstream.toXML(this);
return myxml.equals(oxml);
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
XStream xstream = new XStream();
String myxml = xstream.toXML(this);
return myxml.hashCode();
}
#5
6
Just had to implement comparison of two entity instances revised by Hibernate Envers. I started writing my own differ but then found the following framework.
只需要实现Hibernate Envers修改的两个实体实例的比较。我开始写自己的不同,但后来找到了以下框架。
https://github.com/SQiShER/java-object-diff
https://github.com/SQiShER/java-object-diff
You can compare two objects of the same type and it will show changes, additions and removals. If there are no changes, then the objects are equal (in theory). Annotations are provided for getters that should be ignored during the check. The frame work has far wider applications than equality checking, i.e. I am using to generate a change-log.
您可以比较两个相同类型的对象,它将显示更改,添加和删除。如果没有变化,则对象相等(理论上)。为检查期间应忽略的getter提供注释。框架工作具有比等式检查更广泛的应用程序,即我用于生成更改日志。
Its performance is OK, when comparing JPA entities, be sure to detach them from the entity manager first.
它的性能还可以,比较JPA实体时,请务必先将它们从实体管理器中分离出来。
#6
3
http://www.unitils.org/tutorial-reflectionassert.html
http://www.unitils.org/tutorial-reflectionassert.html
public class User {
private long id;
private String first;
private String last;
public User(long id, String first, String last) {
this.id = id;
this.first = first;
this.last = last;
}
}
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);
#7
2
If your objects implement Serializable you can use this:
如果您的对象实现Serializable,您可以使用:
public static boolean deepCompare(Object o1, Object o2) {
try {
ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
ObjectOutputStream oos1 = new ObjectOutputStream(baos1);
oos1.writeObject(o1);
oos1.close();
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
ObjectOutputStream oos2 = new ObjectOutputStream(baos2);
oos2.writeObject(o2);
oos2.close();
return Arrays.equals(baos1.toByteArray(), baos2.toByteArray());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#8
2
Override The equals() Method
You can simply override the equals() method of the class using the EqualsBuilder.reflectionEquals() as explained here:
您可以使用EqualsBuilder.reflectionEquals()简单地覆盖类的equals()方法,如下所述:
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
#9
1
Your Linked List example is not that difficult to handle. As the code traverses the two object graphs, it places visited objects in a Set or Map. Before traversing into another object reference, this set is tested to see if the object has already been traversed. If so, no need to go further.
您的链接列表示例并不难处理。当代码遍历两个对象图时,它将访问的对象放在Set或Map中。在遍历另一个对象引用之前,将测试此集以查看是否已遍历该对象。如果是这样,就没有必要再往前走了。
I agree with the person above who said use a LinkedList (like a Stack but without synchronized methods on it, so it is faster). Traversing the object graph using a Stack, while using reflection to get each field, is the ideal solution. Written once, this "external" equals() and "external" hashCode() is what all equals() and hashCode() methods should call. Never again do you need a customer equals() method.
我同意上面说过使用LinkedList的人(比如Stack但没有使用synchronized方法,所以它更快)。使用堆栈遍历对象图,同时使用反射来获取每个字段,是理想的解决方案。写一次,这个“外部”等于()和“外部”hashCode()是所有equals()和hashCode()方法应该调用的。您再也不需要客户equals()方法了。
I wrote a bit of code that traverses a complete object graph, listed over at Google Code. See json-io (http://code.google.com/p/json-io/). It serializes a Java object graph into JSON and deserialized from it. It handles all Java objects, with or without public constructors, Serializeable or not Serializable, etc. This same traversal code will be the basis for the external "equals()" and external "hashcode()" implementation. Btw, the JsonReader / JsonWriter (json-io) is usually faster than the built-in ObjectInputStream / ObjectOutputStream.
我编写了一些代码,遍历完整的对象图,列在Google Code上。请参阅json-io(http://code.google.com/p/json-io/)。它将Java对象图序列化为JSON并从中反序列化。它处理所有Java对象,有或没有公共构造函数,Serializeable或Serializable等。这个相同的遍历代码将是外部“equals()”和外部“hashcode()”实现的基础。顺便说一下,JsonReader / JsonWriter(json-io)通常比内置的ObjectInputStream / ObjectOutputStream更快。
This JsonReader / JsonWriter could be used for comparison, but it will not help with hashcode. If you want a universal hashcode() and equals(), it needs it's own code. I may be able to pull this off with a generic graph visitor. We'll see.
这个JsonReader / JsonWriter可以用于比较,但它对hashcode没有帮助。如果你想要一个通用的hashcode()和equals(),它需要它自己的代码。我可以用通用图形访问者来解决这个问题。我们拭目以待。
Other considerations - static fields - that's easy - they can be skipped because all equals() instances would have the same value for static fields, as the static fields is shared across all instances.
其他注意事项 - 静态字段 - 这很容易 - 可以跳过它们,因为所有equals()实例对静态字段都具有相同的值,因为静态字段在所有实例之间共享。
As for transient fields - that will be a selectable option. Sometimes you may want transients to count other times not. "Sometimes you feel like a nut, sometimes you don't."
对于瞬态字段 - 这将是一个可选择的选项。有时您可能希望瞬态计数不是其他时间。 “有时候你感觉自己像个坚果,有时你却没有。”
Check back to the json-io project (for my other projects) and you will find the external equals() / hashcode() project. I don't have a name for it yet, but it will be obvious.
回到json-io项目(对于我的其他项目),你会发现外部的equals()/ hashcode()项目。我还没有它的名字,但很明显。
#10
1
Apache gives you something, convert both objects to string and compare strings, but you have to Override toString()
Apache为您提供了一些东西,将两个对象转换为字符串并比较字符串,但您必须覆盖toString()
obj1.toString().equals(obj2.toString())
Override toString()
覆盖toString()
If all fields are primitive types :
如果所有字段都是原始类型:
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@Override
public String toString() {return
ReflectionToStringBuilder.toString(this);}
If you have non primitive fields and/or collection and/or map :
如果您有非原始字段和/或集合和/或映射:
// Within class
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@Override
public String toString() {return
ReflectionToStringBuilder.toString(this,new
MultipleRecursiveToStringStyle());}
// New class extended from Apache ToStringStyle
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.*;
public class MultipleRecursiveToStringStyle extends ToStringStyle {
private static final int INFINITE_DEPTH = -1;
private int maxDepth;
private int depth;
public MultipleRecursiveToStringStyle() {
this(INFINITE_DEPTH);
}
public MultipleRecursiveToStringStyle(int maxDepth) {
setUseShortClassName(true);
setUseIdentityHashCode(false);
this.maxDepth = maxDepth;
}
@Override
protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
if (value.getClass().getName().startsWith("java.lang.")
|| (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
buffer.append(value);
} else {
depth++;
buffer.append(ReflectionToStringBuilder.toString(value, this));
depth--;
}
}
@Override
protected void appendDetail(StringBuffer buffer, String fieldName,
Collection<?> coll) {
for(Object value: coll){
if (value.getClass().getName().startsWith("java.lang.")
|| (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
buffer.append(value);
} else {
depth++;
buffer.append(ReflectionToStringBuilder.toString(value, this));
depth--;
}
}
}
@Override
protected void appendDetail(StringBuffer buffer, String fieldName, Map<?, ?> map) {
for(Map.Entry<?,?> kvEntry: map.entrySet()){
Object value = kvEntry.getKey();
if (value.getClass().getName().startsWith("java.lang.")
|| (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
buffer.append(value);
} else {
depth++;
buffer.append(ReflectionToStringBuilder.toString(value, this));
depth--;
}
value = kvEntry.getValue();
if (value.getClass().getName().startsWith("java.lang.")
|| (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
buffer.append(value);
} else {
depth++;
buffer.append(ReflectionToStringBuilder.toString(value, this));
depth--;
}
}
}}
#11
0
I guess you know this, but In theory, you're supposed to always override .equals to assert that two objects are truly equal. This would imply that they check the overridden .equals methods on their members.
我想你知道这一点,但理论上,你应该总是覆盖.equals断言两个对象是真正相等的。这意味着他们会检查其成员上被覆盖的.equals方法。
This kind of thing is why .equals is defined in Object.
这就是为什么.equals在Object中定义的原因。
If this were done consistently you wouldn't have a problem.
如果一直这样做,你就不会有问题。
#12
0
Hamcrest has the Matcher samePropertyValuesAs. But it relies on the JavaBeans Convention (uses getters and setters). Should the objects that are to be compared not have getters and setters for their attributes, this will not work.
Hamcrest具有Matcher samePropertyValuesAs。但它依赖于JavaBeans公约(使用getter和setter)。如果要比较的对象不具有其属性的getter和setter,则这将不起作用。
import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
import static org.junit.Assert.assertThat;
import org.junit.Test;
public class UserTest {
@Test
public void asfd() {
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertThat(user1, samePropertyValuesAs(user2)); // all good
user2 = new User(1, "John", "Do");
assertThat(user1, samePropertyValuesAs(user2)); // will fail
}
}
The user bean - with getters and setters
用户bean - 带有getter和setter
public class User {
private long id;
private String first;
private String last;
public User(long id, String first, String last) {
this.id = id;
this.first = first;
this.last = last;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
}
#13
0
A halting guarantee for such a deep comparison might be a problem. What should the following do? (If you implement such a comparator, this would make a good unit test.)
这种深度比较的暂停保证可能是一个问题。以下应该怎么做? (如果你实现这样的比较器,这将是一个很好的单元测试。)
LinkedListNode a = new LinkedListNode();
a.next = a;
LinkedListNode b = new LinkedListNode();
b.next = b;
System.out.println(DeepCompare(a, b));
Here's another:
这是另一个:
LinkedListNode c = new LinkedListNode();
LinkedListNode d = new LinkedListNode();
c.next = d;
d.next = c;
System.out.println(DeepCompare(c, d));
#14
0
I think the easiest solution inspired by Ray Hulha solution is to serialize the object and then deep compare the raw result.
我认为Ray Hulha解决方案启发的最简单的解决方案是序列化对象,然后深入比较原始结果。
The serialization could be either byte, json, xml or simple toString etc. ToString seems to be cheaper. Lombok generates free easy customizable ToSTring for us. See example below.
序列化可以是字节,json,xml或简单的toString等.ToString似乎更便宜。 Lombok为我们生成免费的易于定制的ToSTring。见下面的例子。
@ToString @Getter @Setter
class foo{
boolean foo1;
String foo2;
public boolean deepCompare(Object other) { //for cohesiveness
return other != null && this.toString().equals(other.toString());
}
}