我有以下代码:
private List<String> listOfStrings = new ArrayList<>();
listOfStrings.add("a");
listOfStrings.add("b");
listOfStrings.add("c");
listOfStrings.add("d");
for (String temp : listOfStrings) {
if (temp.equals("c")) {
Collections.swap(listOfStrings, 0, listOfStrings.indexOf(temp));
}
}
列表可能不仅仅是String列表,而且可能是我编写的类所定义的对象列表。我不确定这里的交换,我看到它已编译好并且运行正常,但我不知道它是否安全。
有人对此有任何建议吗?如果我需要进行交换。
我计划使用for (int i = 0; i < size; i++)
进行迭代并使用list.get(i)
来获取项目,但我认为在arraylist上使用list.get(i)
不是一个好主意吗?
任何帮助将不胜感激!在此先感谢!!
答案 0 :(得分:5)
如果你担心ConcurrentModificationException
,是的,在循环中调用swap是安全的。
增强的for循环将在内部使用迭代器,并且迭代器在检测到列表的结构修改时可能会抛出ConcurrentModificationException
,这不是由迭代器本身完成的。虽然您确实修改了列表,但您没有进行结构修改:结构修改是一种修改,其中列表(或后备数组)的大小会发生变化。仅仅设置一个值不被视为结构修改。来自Java API documentation:
(结构修改是添加或删除一个或多个元素的任何操作,或显式调整后备数组的大小;仅设置元素的值不是结构修改。)
但是,在您的情况下使用基于索引的for循环会更快。原因是indexOf(temp)
调用实际上需要找到对象来获取其索引,因此它必须再次遍历列表项。因此,您的算法具有二次运行时间。在基于索引的for循环中,您已经知道要交换的元素的索引,因此这不是必需的,并且您具有线性运行时间。
答案 1 :(得分:2)
我猜您的问题是&#39; 这可能会引发ConcurrentModificationException
?&#39;
要回答这个问题,您应该知道在集合中抛出ConcurrentModificationException
的时间和原因。
以ArrayList
为例:
ArrayList
有一个字段modCount
,表示modification count
。当调用某些导致结构修改到ArrayList
的方法时,例如add()
/ remove()
,会增加modCount``(modCount++)
。ArrayList.Iterator
有一个字段expectedModCount
,在创建时由ArrayList modCount
分配。调用next()
或remove()
时,会将expectedModCount
与modCount
进行比较,如果不等于,则会抛出ConcurrentModificationException
。 Collections.swap
的源代码:
public static void swap(List<?> list, int i, int j) {
final List l = list;
l.set(i, l.set(j, l.get(i)));
}
Collections.swap
调用ArrayList的set()
方法,该方法不会增加modCount
,因此不会导致ConcurrentModificationException
。
答案 2 :(得分:0)
swap(List<?> list, int i, int j)
将列表和两个索引作为参数放入for循环应该不是问题。
内部交换方法执行以下操作:
final List l = list;
l.set(i, l.set(j, l.get(i)));
它只是将一个项目从一个索引放到另一个索引。
对于线程安全:你需要让List List Safe不是交换方法,使用下面的代码来使你的列表线程安全。然后使用list。
Collections.synchronizedList(yourList).