我有一个带有列表的实体:
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)
@JoinColumn(name="orderId", nullable=false)
private List<Item> items;
}
@Entity
@Data
public class Item {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@EqualsAndHashCode.Exclude
private Long id;
private String description;
}
我有一个服务,用于检查两个订单是否具有相同的项目,如果有则返回这些项目;否则返回null:
public List<Item> getItemsIfSame(Order order1, Order order2) {
if (order1.getItems() != null && order1.getItems().equals(order2.getItems())) {
return order1.getItems();
}
return null;
}
我有一个单元测试,其中order1和order2具有相同的项目。如预期的那样,项目列表从getItemsIfSame
方法返回。
但是,当我运行我的应用程序并通过两个带有相同项目的订单时,返回null。经过调试和研究之后,我发现Order
方法getItems
返回的实际类型是org.hibernate.collection.internal.PersistentBag
。其文档指出:
包不遵守集合API,并且进行JVM实例比较来实现相等性。不必为简单的equals()操作初始化集合就可以打破语义。
并在源代码中进行确认,它仅调用Object
的equals方法(即使它实现了List
)。
我想我可以将所有元素从PersistentBag
复制到ArrayList
,然后进行比较,但是有时我正在检查具有嵌套属性和列表的对象的相等性。有没有更好的方法来检查实体之间的列表是否相等?
答案 0 :(得分:0)
解决方案1::使用番石榴的Iterables#elementsEqual
Iterables.elementsEqual(
order1.getItems() != null ? order1.getItems() : new ArrayList<>(),
order2.getItems() != null ? order2.getItems() : new ArrayList<>());
解决方案2:使用java.util.Objects#deepEquals
Objects.deepEquals(
order1.getItems() != null ? order1.getItems().toArray() : order1,
order2.getItems() != null ? order2.getItems().toArray() : order2);
解决方案3:使用新的ArrayList
对象
(order1.getItems() != null ? new ArrayList(order1.getItems()) : new ArrayList())
.equals(order2.getItems() != null ? new ArrayList(order2.getItems()) : new ArrayList());
解决方案#4 使用Apache的CollectionUtils#isEqualCollection
CollectionUtils.isEqualCollection(
order1.getItems() != null ? order1.getItems() : new ArrayList(),
order2.getItems() != null ? order2.getItems() : new ArrayList());
请注意,List#toArray
方法的Javadocs声明如下:
以正确的顺序返回一个包含此列表中所有元素的数组(从第一个元素到最后一个元素)。 返回的数组将是“安全的”,因为此列表不保留对其的引用。 (换句话说,此方法必须分配一个新数组)。因此,调用者可以自由修改返回的数组。
这样,与解决方案2、3和4相比,使用Iterables
就地比较列表可能会使用更少的内存,而解决方案2、3和4都会隐式或显式分配新的列表或数组。
空检查也可以移出三元对象,但是需要对两个order
对象执行,因为所有这些解决方案都涉及到调用非空安全(Iterables#elementsEqual
,Lists#toArray
,new ArrayList(Collection<?> collection)
,CollectionUtils.isEqualCollection
用null调用时都会抛出NullPointerExceptions。