Android Room中相同List调用equals返回false的问题排查
问题分析与解决方案
首先咱们拆解你遇到的两个核心问题:LiveData Observer被触发的原因,以及内容相同的列表equals返回false的根本原因,然后一步步给出解决办法。
为什么Observer会被触发?
这是Room的正常设计逻辑——当关联的任意表(这里是右表piece_table)发生数据变更时,Room无法精准判断这个变更是否会影响你的查询结果(比如你插入的第三行外键不指向左表,确实不影响),所以它会重新执行查询,生成新的结果列表并通知Observer。这个机制是合理的,因为Room不可能每次都去解析WHERE条件和变更的关联性,只能通过重新查询来保证数据的准确性。
为什么内容相同的列表equals返回false?
这才是你疑惑的核心,根源在于两点:
- Room每次查询都会生成全新的实例:哪怕数据内容完全一致,新列表是全新的
ArrayList实例,旧列表是存在HashMap里的旧实例;而List的equals方法是遍历元素逐个比较,所以如果元素的equals判定为不等,整个列表的equals就会返回false。 - 实体类未重写
equals和hashCode:Java默认的Object.equals()是比较对象的内存地址,而不是字段内容。你定义的PieceQuantityAndPiece、PieceQuantity和Piece都没有重写这两个方法,所以哪怕两个对象的字段值完全一样,只要是不同实例,equals就会返回false,最终导致整个列表的equals判定失败。
解决方案
1. 给所有实体类重写equals和hashCode
这是最根本的解决方法,要确保所有涉及的实体类都正确重写这两个方法,比较所有字段值。
示例:重写PieceQuantityAndPiece
public class PieceQuantityAndPiece implements EntityInterface { private static final String TAG = "PieceQuantityAndPiece"; @Embedded public PieceQuantity pieceQuantity; @Relation( parentColumn = "child_piece_id", entityColumn = "piece_id" ) public Piece piece; public PieceQuantityAndPiece() { } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PieceQuantityAndPiece that = (PieceQuantityAndPiece) o; // 递归比较内嵌实体,前提是PieceQuantity和Piece也重写了equals return Objects.equals(pieceQuantity, that.pieceQuantity) && Objects.equals(piece, that.piece); } @Override public int hashCode() { return Objects.hash(pieceQuantity, piece); } }
示例:重写Piece类(PieceQuantity同理)
@Entity(tableName = "piece_table") public class Piece { @PrimaryKey public int piece_id; public int _total; public long _date_of_last_edit; // 其他字段... @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Piece piece = (Piece) o; // 比较所有字段,包括主键和业务字段 return piece_id == piece.piece_id && _total == piece._total && _date_of_last_edit == piece._date_of_last_edit; } @Override public int hashCode() { return Objects.hash(piece_id, _total, _date_of_last_edit); } }
2. 用Transformations.distinctUntilChanged减少不必要的回调
如果你不想每次内容没变都触发onFound方法,可以用Room提供的Transformations.distinctUntilChanged包装LiveData,它会自动比较前后结果的equals,只有真正变化时才通知Observer:
// 获取原始LiveData LiveData<List<PieceQuantityAndPiece>> originalLiveData = yourDao.getPieceQuantityAndPiecesAtFolder(folderId); // 包装成仅在数据真正变化时通知的LiveData LiveData<List<PieceQuantityAndPiece>> distinctLiveData = Transformations.distinctUntilChanged(originalLiveData); // 观察包装后的LiveData distinctLiveData.observe(this, xes -> onFound(xes, liveDataId));
3. 关于HashMap的疑问
HashMap本身不是导致equals失败的原因——哪怕你不用HashMap,直接拿旧列表和新列表比较,只要实体类没重写equals,结果还是false。HashMap只是一个存储容器,核心问题还是实体类的equals实现。
内容的提问来源于stack exchange,提问作者Delark
相关产品推荐
相关产品推荐

