【发布时间】:2020-10-10 19:12:55
【问题描述】:
我目前正在学习来自 Java 的 Kotlin。在Java中,做
for(String s:stringList){
if(condition == true) stringList.remove(s);
}
不起作用,因为您只能使用 for each 读取数据。这也适用于 Kotlin 吗?
【问题讨论】:
我目前正在学习来自 Java 的 Kotlin。在Java中,做
for(String s:stringList){
if(condition == true) stringList.remove(s);
}
不起作用,因为您只能使用 for each 读取数据。这也适用于 Kotlin 吗?
【问题讨论】:
这与 Java 中的限制相同 - 当您迭代集合时,您只能通过 Iterator 的 remove() 方法对其进行修改
从底层集合中移除此迭代器返回的最后一个元素(可选操作)。每次调用 next() 时只能调用一次此方法。 如果在迭代过程中通过调用此方法以外的任何方式修改了底层集合,则迭代器的行为是不确定的。
Kotlin 有 its own equivalent(请记住 Kotlin 使用可变和不可变列表的概念,因此您只能使用 MutableList 来做到这一点)
foreach / 增强的for 结构的问题在于它没有公开您需要调用remove() 的Iterator,并且不允许您在集合本身上调用remove() .和 Kotlin 一样 - 所以你的选择基本上是
for/forEach(但不能修改集合)iterator() 方法并自己对其进行迭代(现在您可以在其上调用remove(),但有我引用的限制)最后一个是在 Kotlin 中鼓励你做的事情——不要修改集合,而是尝试使用不可变的集合并将它们转换为新的集合:
stringList.filterNot { condition }
如果您熟悉所有 collection functions(是的,有很多 个),您就会了解将 A 转换为 B 的方法以及便利功能可以帮助您做到这一点(例如filterTo,它允许您提供MutableList 来填充您想要保留的项目)。
如果您确实想就地修改可变集合,这是可能的,但您必须自己管理它,因为它本质上是危险的,并且系统无法保证您安全地进行操作。
【讨论】:
filter() 或类似名称来创建新列表。
removeAll(elements) 调用 - 所以如果就地改变列表是关于内存效率或其他问题,那么请记住这一点。
removeAll()的谓词版本的当前实现非常高效:对于RandomAccess列表,例如ArrayList,它对列表进行单次扫描,复制项目就地。 (如果不是RandomAccess,它会回退到在迭代器上调用remove()。) 当然,这只是一个实现细节,不能保证;但未来的版本不太可能降低性能。
是的,如果你用 Kotlin 重写这段代码,你会遇到同样的并发修改问题:
for (s in stringList) {
if(condition == true) stringList.remove(s)
}
为避免此问题,您可以从可变列表中获取可变迭代器,并使用它随时删除元素。但与 Java 不同的是,您不必使用 hasNext()/next() 调用手动迭代它——您可以使用相同的 for 运算符来迭代它:
val iterator = stringList.iterator()
for (s in iterator) {
if (condition == true) iterator.remove()
}
【讨论】: