Grails3/Groovy移除列表首元素迭代报错,添加判断后解决求因
问题解析与原因说明
嘿,这个问题其实和Groovy本身的bug没关系,核心是Grails GORM的懒加载集合特性在搞鬼,我来帮你拆解清楚:
原代码的问题根源
你用到的listUsers应该是GORM查询返回的结果集合(比如User.list()这类查询的输出)。Grails的MongoDB GORM实现返回的列表通常是懒加载的代理集合——这种集合不会一开始就把数据库里的所有用户对象都加载到内存,而是在你第一次迭代元素、访问具体位置,或者调用toString()(比如println的时候),才会真正去数据库拉取数据并实例化所有对象。
原代码里直接调用listUsers.remove(User.findByUsername(getPrincipal().username))时:
- 如果当前登录用户刚好在列表的第一个位置(索引0),集合可能只加载了第一个元素,后续元素还处于未实例化的状态,导致
remove操作在匹配对象时因集合内部元素状态不完整抛出异常。 - 而添加
println(listUsers)后,这个操作会触发集合的toString()方法,进而强制迭代整个集合,把所有用户对象都加载到内存转换成普通的内存集合,这时候remove就能正确找到并移除目标对象了。
修改后代码为什么能解决问题
你改成先判断if (loggedUser in listUsers),这个in操作符会触发对listUsers的完整迭代,相当于提前把懒加载的代理集合转换成了内存中已完全实例化的元素集合。等执行到listUsers.remove(loggedUser)时,集合里的所有对象都已经加载完成,能正确匹配并移除目标用户,自然就不会报错了。
额外的优化建议
其实还有更高效的写法:直接在查询阶段就排除当前登录用户,不用在内存里做移除操作,既避免了懒加载的问题,也提升了性能:
def listUsers = User.where { username != getPrincipal().username }.list()
这样返回的集合本身就不包含当前用户,后续处理也更省心~
内容的提问来源于stack exchange,提问作者Diogo Filipe
相关产品推荐
相关产品推荐

