使用assertj-core递归比较大量自定义对象集合性能异常求助
解决AssertJ递归比较大集合卡顿的方案
核心原因
AssertJ的递归比较默认会遍历对象所有字段进行深度对比,当集合元素数量大、嵌套层次深时,全字段递归的开销会指数级增长。即使设置了ignoreCollectionOrder(false),只是按索引匹配元素,但每个元素内部仍会执行全字段递归比较,依然会产生大量性能开销。
可行优化方案
1. 给实体类重写equals()和hashCode()
AssertJ在递归比较时会优先调用对象的equals()方法,若未重写才会 fallback 到全字段递归比较。重写后,集合元素的比较会直接使用equals(),可大幅减少递归开销。
示例代码:
import java.util.ArrayList; import java.util.Objects; class Owner { String id; String name; ArrayList<Assignment> assignments; Owner(String id, String name, ArrayList<Assignment> assignments) { this.id = id; this.name = name; this.assignments = assignments; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Owner owner = (Owner) o; return Objects.equals(id, owner.id) && Objects.equals(name, owner.name) && Objects.equals(assignments, owner.assignments); } @Override public int hashCode() { return Objects.hash(id, name, assignments); } } class Assignment { String assignmentName; String assignmentDescription; Assignment(String assignmentName, String assignmentDescription) { this.assignmentName = assignmentName; this.assignmentDescription = assignmentDescription; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Assignment that = (Assignment) o; return Objects.equals(assignmentName, that.assignmentName) && Objects.equals(assignmentDescription, that.assignmentDescription); } @Override public int hashCode() { return Objects.hash(assignmentName, assignmentDescription); } }
2. 指定集合元素的匹配主键(避免全字段预比较)
如果不想重写equals(),可以通过RecursiveComparisonConfiguration指定集合元素的唯一标识字段,让AssertJ先按主键匹配元素,再对比其他字段,避免对所有元素进行全字段递归匹配。
示例代码:
RecursiveComparisonConfiguration config = RecursiveComparisonConfiguration.builder() // Owner集合按id字段匹配 .withCollectionComparatorForType(Comparator.comparing(Owner::getId), Owner.class) // Assignment集合按assignmentName字段匹配 .withCollectionComparatorForType(Comparator.comparing(Assignment::getAssignmentName), Assignment.class) .build(); List<OwnerDifference> differences = RecursiveComparisonDifferenceCalculator.determineDifferences(list1owners, list2owners, config);
3. 限制比较字段(只对比需要的字段)
忽略不必要的字段,或仅指定需要比较的字段,减少比较的字段数量和递归深度。
- 忽略指定字段:
RecursiveComparisonConfiguration config = RecursiveComparisonConfiguration.builder() // 忽略Assignment的description字段 .withIgnoredFields("assignments.assignmentDescription") .build();
- 仅对比指定字段:
RecursiveComparisonConfiguration config = RecursiveComparisonConfiguration.builder() .withComparingOnlyFields("id", "name", "assignments.assignmentName") .build();
4. 关闭不必要的递归特性
如果业务场景允许,关闭会增加开销的特性,比如忽略null值差异:
RecursiveComparisonConfiguration config = RecursiveComparisonConfiguration.builder() .withIgnoreExpectedNulls(true) // 忽略预期对象中的null字段 .withIgnoreActualNulls(true) // 忽略实际对象中的null字段 .build();
内容的提问来源于stack exchange,提问作者Amit
相关产品推荐
相关产品推荐

