问题描述
如果我有两个来自以下两个不同类的实例(具有相似的属性),我可以使用Hamcrest的containsInAnyOrder
来匹配其属性吗?
class Something{
int id;
int name;
}
class GsonSomething{
int id;
int name;
}
我可以使用containsInAnyOrder
或任何其他方法来比较2个类中的2个列表吗?
List<Something> somethingList; //[ {id:1,name: "one"},{id:1,name: "two"} ]
List<GsonSomething> gsonSomethingList; //[ {id:1,name: "two"} ]
此方法的用例将用于测试方法中,以比较结果与预期结果。我使用了以下方法:
for(Something s : somethingList){
GsonSomething gs = gsonSomethingList.stream().filter(p -> p.id.equals(s.id)).findFirst();
assertEquals(s.name,gs.name);
//assert other attributes
}
解决方法
通过扩展http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/BaseMatcher.html来创建自己的匹配器,并将其用于断言中。
,不直接,不。
为什么不呢?
Java认为类型名称空间是神圣的,其他所有内容都只与它所在的类型有关。换句话说,在Java中,这两种方法是:
class Gun() { void shoot(Person p); }
class Camera() { void shoot(Person p); }
被认为是完全不相关的,对于碰巧具有相同名称的字段也是如此:并不意味着它们完全相关。
好的,那我该怎么做?
您将必须尽力将输入转换为可以比较的内容。
幸运的是,这相对简单。如果这是您要比较的单个属性(例如,int id
值),则只需将两个值都转换为该值,然后进行比较即可。
如果您具有“复合键”(您希望id
和name
相等),则需要某种可以容纳这些键的类型。大概GsonSomething
和Something
本身都可以胜任工作类型,因此您实际上只需要将GsonSomething
的列表转换为{{1}的列表},然后您可以进行比较。
那我的解决方案怎么办
您的解决方案有两个问题。
一个明显的人:它做得不好。如果gsonSomethingList中有一个元素根本没有出现在somethingList中,则您的测试仍将通过,这是不正确的。
一个不太麻烦的问题:它非常慢;这是O(n ^ 2)的速度。如果输入开始达到5位数字(输入1万以上),您会注意到。当他们达到7时,该测试将花费极长时间。
那有什么更好的方法呢?
要加快比较速度,必须将它们放在一组中,这需要适当的equals和哈希码impls。因此,让我们从此处开始,并使Something
为记录类别:
Something
如果您不使用lombok,也许:
@lombok.EqualsAndHashCode
class Something {
int id;
int name;
}
现在我们有了,我们可以将一个转换为另一个,并使用集合,这样可以更快地完成这样的工作(实际上是按算法计算的):
class Something {
int id;
int name;
@Override public boolean equals(Object other) {
if (other == this) return true;
if (other == null) return false;
if (other.getClass() != Something.class) return false;
Something o = (Something) other;
return o.id == this.id && o.name == this.name;
// NB: Use .equals() and add null checks if `name` is String!
}
@Override public int hashCode() {
return (31 + id) * 31 + name;
}
}
,现在您可以使用包含Set<Something> a = ....;
List<GsonSomething> rawB = ...;
Set<Something> b = rawB.stream()
.map(b -> new Something(b.getId(),b.getName())
.collect(Collectors.asSet());
和a
的containsInAnyOrder。
我会进行一些重构。我首先创建一个基类,使Something
和GsonSomething
类对其进行扩展。在基类中,我将覆盖equals
方法。
abstract class BaseSomething {
int id;
String name; //This should definitely be a String type
//getters and setters
@Override
public boolean equals(Object other) {
if (other == this) return true;
if (other == null) return false;
if (!(other instanceof BaseSomething)) return false;
BaseSomething o = (BaseSomething) other;
return o.id == this.id && o.name.equals(this.name);
}
}
class Something extends BaseSomething {
}
class GsonSomething extends BaseSomething {
}
现在,这很容易。您的两个List
字段现在为:
List<BaseSomething> somethingList; //[ {id:1,name: "one"},{id:1,name: "two"} ]
List<BaseSomething> gsonSomethingList; //[ {id:1,name: "two"} ]
只需调用Hamcrest方法即可完成工作:
assertThat(gsonSomethingList,containsInAnyOrder(somethingList.toArray(new BaseSomething[0]));