说我有一个像这样的列表:
List<String> list = new ArrayList<>();
list.add("a");
list.add("h");
list.add("f");
list.add("s");
在遍历此列表时,我想在列表末尾添加一个元素。但我不想迭代新添加的元素,我希望迭代到列表的初始大小。
for (String s : list)
/* Here I want to add new element if needed while iterating */
有人可以建议我怎么做?
答案 0 :(得分:39)
您无法使用foreach语句。 foreach在内部使用迭代器:
此类的迭代器和listIterator返回的迭代器 方法是快速失败的:如果列表在结构上被修改了 创建迭代器之后的时间,除了通过之外的任何方式 迭代器自己删除或添加方法,迭代器会抛出一个 ConcurrentModificationException的。
(来自ArrayList javadoc)
在foreach语句中,您无权访问迭代器的add方法,并且在任何情况下仍然不是您想要的add类型,因为它最后没有附加。您需要手动遍历列表:
int listSize = list.size();
for(int i = 0; i < listSize; ++i)
list.add("whatever");
请注意,这仅适用于允许随机访问的列表。您可以通过检查列表是否实现RandomAccess标记接口来检查此功能。 ArrayList具有随机访问权限。链表不会。
答案 1 :(得分:8)
只需迭代旧式方式,因为您需要显式索引处理:
List myList = ...
...
int length = myList.size();
for(int i = 0; i < length; i++) {
String s = myList.get(i);
// add items here, if you want to
}
答案 2 :(得分:7)
遍历列表副本并将新元素添加到原始列表中。
for (String s : new ArrayList<String>(list))
{
list.add("u");
}
请参阅 How to make a copy of ArrayList object which is type of List?
答案 3 :(得分:3)
您可以迭代原始列表的副本(克隆):
List<String> copy = new ArrayList<String>(list);
for (String s : copy) {
// And if you have to add an element to the list, add it to the original one:
list.add("some element");
}
请注意,甚至无法在迭代时向列表中添加新元素,因为它会产生ConcurrentModificationException
。
答案 4 :(得分:1)
我是通过将元素添加到新的空tmp列表,然后使用addAll()
将tmp列表添加到原始列表来实现的。这可以防止不必要地复制大型源列表。
想象一下当OP的原始列表中有几百万个项目时会发生什么;有一段时间你会把记忆吸得两倍。
除了节省资源之外,这种技术还可以防止我们不得不求助于80s风格的for循环并使用有效的数组索引,这些索引在某些情况下可能没有吸引力。
答案 5 :(得分:0)
为了帮助解决这个问题,我创建了一个函数来让这个更容易实现。
public static <T> void forEachCurrent(List<T> list, Consumer<T> action) {
final int size = list.size();
for (int i = 0; i < size; i++) {
action.accept(list.get(i));
}
}
示例
List<String> l = new ArrayList<>();
l.add("1");
l.add("2");
l.add("3");
forEachCurrent(l, e -> {
l.add(e + "A");
l.add(e + "B");
l.add(e + "C");
});
l.forEach(System.out::println);