在arraylist for循环中进行Collections.swap()是否安全?

时间:2016-04-25 06:05:44

标签: java list for-loop arraylist collections

我有以下代码:

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)不是一个好主意吗?

任何帮助将不胜感激!在此先感谢!!

3 个答案:

答案 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()时,会将expectedModCountmodCount进行比较,如果不等于,则会抛出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).