[英]How to compare two Objects in Java that also have lists in its properties
[英]How to compare 2 lists of different objects with similar properties
如果我有來自如下 2 個不同類的 2 個實例(具有相似的屬性),我可以使用 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: "one"}, {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); }
被認為是完全不相關的,對於碰巧具有相同名稱的字段也是如此:根本不意味着它們是相關的。
您將不得不 go 將您的輸入轉換為可以比較的東西。
幸運的是,這相對簡單。 如果它是您要比較的單個屬性(例如, int id
值),則只需將兩個值都轉換為該值,然后運行比較。
如果你有“復合鍵”(你希望id
和name
都相等),你需要一些可以保存這些鍵的類型。 大概GsonSomething
和Something
本身都是可以完成這項工作的類型,因此您實際上只需要將GsonSomething
列表轉換為Something
列表,然后您就可以進行比較。
您的解決方案有兩個問題。
最明顯的是:它沒有做好正確的工作。 如果 gsonSomethingList 中有一個元素根本沒有出現在 somethingList 中,你的測試仍然會通過,這是不正確的。
問題稍微少一點:它非常慢; 這是 O(n^2) 速度。 如果輸入開始達到 5 位數(10k 條目及以上 - 在那個范圍內),您會注意到。 一旦他們達到 7,這個測試就開始花費非常長的時間。
為了加速比較,這些需要在一個集合中,這需要適當的 equals 和 hashcode impls。 那么讓我們從這里開始,讓Something
are class of record:
@lombok.EqualsAndHashCode
class Something {
int id;
int name;
}
如果lombok不是您正在使用的東西,也許:
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());
現在您可以將例如 containsInAnyOrder 與a
和b
一起使用。
我會做一些輕微的重構。 我會首先創建一個基礎 class,使Something
和GsonSomething
class 擴展它。 在基礎 class 中,我將覆蓋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: "one"}, {id:1, name: "two"} ]
對 Hamcrest 方法的簡單調用應該可以完成這項工作:
assertThat(gsonSomethingList, containsInAnyOrder(somethingList.toArray(new BaseSomething[0]));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.