这是我的Java代码:
List<Object> objects = new ArrayList();
// Assign values to objects
...
for (int i = 0; i < objects.size(); i++) {
Object object = objects.get(i);
...
}
我有两个问题:
objects.size()
仅计算一次,还是计算每次循环?objects.size()
,那么如果其他线程同时更改它而没有多线程保护,则代码可能会崩溃。 我说错了吗?
答案 0 :(得分:12)
数目:
objects.size()
被称为(无论是计算取决于您不应该关心的ArrayList实现)真实答案:
您不必关心,以下是您不必关注的方式:
foreach
语法,这意味着您不必使用索引等 - 它已为您完成:
for (Object object : objects) {
// do something with each object
}
答案 1 :(得分:7)
是的,每次计算。如果你有另一个线程改变对象列表的大小,循环条件将不断变化。
答案 2 :(得分:2)
是的,当你在每次计算的循环条件中使用objects.size()
时。更好的方法是在进入循环之前将其保存在变量中;
像int
limit=objects.size();
for (int i = 0; i < limit; i++) {
Object object = objects.get(i);
...
}
如果你有另一个线程,它可能会改变它,但使用上面的选项它不会影响或崩溃你的程序。
答案 3 :(得分:1)
是的,它会每次计算
如果你查看循环语句,在第一个语句中它将设置计数器初始值 然后它将检查最大值然后它将执行循环体然后它将增加计数器的值然后再次检查最大值。每次检查最大值时都会调用大小方法。
答案 4 :(得分:1)
- 在声明循环之前,objects.size()只计算过一次,还是计算每次循环?
醇>
每一次。
- 如果每个循环都计算了objects.size(),那么如果其他线程在没有多线程保护的情况下同时更改它,则代码可能会崩溃。
醇>
是。或者至少,你可能得到一个ConcurrentModificationException
,没有任何合理的方法来处理它。
请注意,即使您缓存objects.size()
,也可能发生这种情况,但现在.get()
将失败,因为您尝试获取不再存在的索引。 objects.size()
更改是因为某些内容已从容器中删除或添加到容器中。
在迭代集合时不要修改集合。
答案 5 :(得分:1)
从概念上讲,可以在每个循环上评估objects.size()。但是,由于方法很短,因此它可以内联和缓存,因为它不是一个易变的变量。即另一个线程可以改变它,但不能保证,如果它确实你会看到改变。
保存大小的简短方法是使用以下内容。
for (int i = 0, size = objects.size(); i < size; i++) {
Object object = objects.get(i);
...
}
但是,如果您担心另一个线程可能会更改大小,则此方法仅在添加对象时保护您。如果删除了一个对象,当您尝试访问现在超出列表末尾的值时,仍然可以获得异常。
使用CopyOnWriteArrayList避免了这些问题(假设您使用了Iterator),但使写入更加昂贵。