This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 8 years ago.
In Java, is it legal to call remove on a collection when iterating through the collection using a foreach loop? For instance:
List<String> names = ....
for (String name : names) {
// Do something
names.remove(name).
}
As an addendum, is it legal to remove items that have not been iterated over yet? For instance,
//Assume that the names list as duplicate entries
List<String> names = ....
for (String name : names) {
// Do something
while (names.remove(name));
}
To safely remove from a collection while iterating over it you should use an Iterator.
For example:
List<String> names = ....
Iterator<String> i = names.iterator();
while (i.hasNext()) {
String s = i.next(); // must be called before you can call i.remove()
// Do something
i.remove();
}
From the Java Documentation :
The iterators returned by this class's iterator and listIterator
methods are fail-fast: if the list is structurally modified at any
time after the iterator is created, in any way except through the
iterator's own remove or add methods, the iterator will throw a
ConcurrentModificationException. Thus, in the face of concurrent
modification, the iterator fails quickly and cleanly, rather than
risking arbitrary, non-deterministic behavior at an undetermined time
in the future.
Perhaps what is unclear to many novices is the fact that iterating over a list using the for/foreach constructs implicitly creates an iterator which is necessarily inaccessible. This info can be found here
You don't want to do that. It can cause undefined behavior depending on the collection. You want to use an Iterator directly. Although the for each construct is syntactic sugar and is really using an iterator, it hides it from your code so you can't access it to call Iterator.remove.
The behavior of an iterator is
unspecified if the underlying
collection is modified while the
iteration is in progress in any way
other than by calling this method.
Instead write your code:
List<String> names = ....
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String name = it.next();
// Do something
it.remove();
}
Note that the code calls Iterator.remove, not List.remove.
Addendum:
Even if you are removing an element that has not been iterated over yet, you still don't want to modify the collection and then use the Iterator. It might modify the collection in a way that is surprising and affects future operations on the Iterator.
for (String name : new ArrayList<String>(names)) {
// Do something
names.remove(nameToRemove);
}
You clone the list names and iterate through the clone while you remove from the original list. A bit cleaner than the top answer.
The java design of the "enhanced for loop" was to not expose the iterator to code, but the only way to safely remove an item is to access the iterator. So in this case you have to do it old school:
for(Iterator<String> i = names.iterator(); i.hasNext();) {
String name = i.next();
//Do Something
i.remove();
}
If in the real code the enhanced for loop is really worth it, then you could add the items to a temporary collection and call removeAll on the list after the loop.
EDIT (re addendum): No, changing the list in any way outside the iterator.remove() method while iterating will cause problems. The only way around this is to use a CopyOnWriteArrayList, but that is really intended for concurrency issues.
The cheapest (in terms of lines of code) way to remove duplicates is to dump the list into a LinkedHashSet (and then back into a List if you need). This preserves insertion order while removing duplicates.
I didn't know about iterators, however here's what I was doing until today to remove elements from a list inside a loop:
List<String> names = ....
for (i=names.size()-1;i>=0;i--) {
// Do something
names.remove(i);
}
This is always working, and could be used in other languages or structs not supporting iterators.
Yes you can use the for-each loop,
To do that you have to maintain a separate list to hold removing items and then remove that list from names list using removeAll() method,
List<String> names = ....
// introduce a separate list to hold removing items
List<String> toRemove= new ArrayList<String>();
for (String name : names) {
// Do something: perform conditional checks
toRemove.add(name);
}
names.removeAll(toRemove);
// now names list holds expected values
Make sure this is not code smell. Is it possible to reverse the logic and be 'inclusive' rather than 'exclusive'?
List<String> names = ....
List<String> reducedNames = ....
for (String name : names) {
// Do something
if (conditionToIncludeMet)
reducedNames.add(name);
}
return reducedNames;
The situation that led me to this page involved old code that looped through a List using indecies to remove elements from the List. I wanted to refactor it to use the foreach style.
It looped through an entire list of elements to verify which ones the user had permission to access, and removed the ones that didn't have permission from the list.
List<Service> services = ...
for (int i=0; i<services.size(); i++) {
if (!isServicePermitted(user, services.get(i)))
services.remove(i);
}
To reverse this and not use the remove:
List<Service> services = ...
List<Service> permittedServices = ...
for (Service service:services) {
if (isServicePermitted(user, service))
permittedServices.add(service);
}
return permittedServices;
When would "remove" be preferred? One consideration is if gien a large list or expensive "add", combined with only a few removed compared to the list size. It might be more efficient to only do a few removes rather than a great many adds. But in my case the situation did not merit such an optimization.
Those saying that you can't safely remove an item from a collection except through the Iterator aren't quite correct, you can do it safely using one of the concurrent collections such as ConcurrentHashMap.
Try this 2. and change the condition to "WINTER" and you will wonder:
public static void main(String[] args) {
Season.add("Frühling");
Season.add("Sommer");
Season.add("Herbst");
Season.add("WINTER");
for (String s : Season) {
if(!s.equals("Sommer")) {
System.out.println(s);
continue;
}
Season.remove("Frühling");
}
}
It's better to use an Iterator when you want to remove element from a list
because the source code of remove is
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null;
so ,if you remove an element from the list, the list will be restructure ,the other element's index will be changed, this can result something that you want to happened.
Use
.remove() of Interator or
Use
CopyOnWriteArrayList
Related
I am aware of the conventional iterator creation-usage for a List<String> list as below:
//Conventional-style
Iterator<String> iterator = list.iterator()
while(iterator.hasNext()){
String string = iterator.next();
//...further code goes here
}
However, in the accepted answer of Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop, I came across this unusual for loop usage with Iterator:
//Unconventional for loop style
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
String string = iterator.next();
//...further code goes here
}
Now, I'd like to know:
Does this unconventional style create the iterator on the collection for each iteration over and over again? Or is it somehow a special kind of intelligent for-loop, which creates the iterator once and reuses it?
If it creates an iterator each time, shouldn't it be a performance concern?
Can we replace the while loop line in the conventional style with
for(;iterator.hasNext();), if I were to use a for loop only?
PS: I am well aware of the enhanced for loop use on a collection. I am looking at this with the intention of 'safe' removal of elements, without causing a ConcurrentModificationException.
The idiom you call "unconventional" is actually the recommended one because it restricts the scope of the iterator variable to the loop where it is used.
The iterator is created once, before the loop begins. This follows from the general semantics of the for loop, which I warmly advise you get acquainted with.
You can, but you would not be recommended to. Such an idiom would be a pointless obfuscation of the while idiom.
Finally, note that for 99% of use cases all of the above is moot because you really should be using either the enhanced for loop or Java 8 forEach.
Java is derived from C, and thus for (A; B; C) { P; } has the same semantics as A; while (B) { P; C; }. The only difference is the scope of the variables. In particular, the A part is only executed once. So your two code examples do exactly the same, but in the for-variant the scope of the variable is restricted.
The more modern way of iterating through a collection is the enhance for loop:
for (String string : list) {
...
}
However, if you want to delete or change items while iterating through it, you still need the iterator version. For example:
for (Iterator<String> it = list.iterator(); it.hasNext();) {
String string = it.next();
if (someFunction(string)) {
it.delete();
}
}
has no enhanced for-loop equivalent.
1.
No, it does not create an iterator over and over again.. This was the perfectly fine style before Java included the interface Iterable<T>.
If you want to remove an item while iterating over the collection you have to use the iterator.remove() method if it is provided.. Because otherwise a ConcurrentModificationException will be thrown.
If you do not want to remove an Item while iterating over the collection then you should just use the for each concept, which is provided by every collection that implements the Iterable<T> interface. (link in the end for more information)
for (String s : yourList) {
... // do something with the string
}
2.
Yes!! Use the for loop idiom. But as I said, if you do not want to use the iterator.remove() operation, but just want to iterate over the collection, you should use the provided for each concept.
You can find a lot of information on the downsides of the iterator.next() approach here and why the newly integrated for:each concept is better:
https://docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
I have a linkedlist where each element has key and value(ArrayList<dataStructure>). I want to merge the elements having same key.
Iterator<CElem> oItr = linkedList.iterator();
{
while (oItr.hasNext())
{
CElem outer = oItr.next();
Iterator<CElem> iItr = linkedList.iterator();
{
while (iItr.hasNext())
{
CElem inner = iItr.next();
if (outer.equals(inner))
continue;
if (outer.getKey().equals(inner.getKey()))
{
outer.getValues().addAll(inner.getValues());
iItr.remove();
}
}
}
}
}
Though I am using the iterators remove methog getting a java.util.ConcurrentModificationException. What should be changed to get rid of this.
You remove element with one of your iterators, thus the second of them does not know about this removal and ConcurrentModificationException is thrown
BTW:
you should consider using some multimap in place of list that is having key-values pairs
Add the elements you want to remove in another List and then loop in that list at the end to remove these elements.
Alternatively, use a Map/Set.
Both your iterators are traversing the linked list
Iterator<CElem> oItr = linkedList.iterator();
....
Iterator<CElem> iItr = linkedList.iterator();
probably iItr should be for the inner array list?
UPDATE Scratch above answer I misread the question. The challenge though is that you have two iterators traversing the list, so while you use one iterator's remove() method, the other still detects the concurrent modification.
Normally, to remove duplicates from a list, you can just run them through a Set (e.g. a HashSet) but that won't work for you as it's only key duplication, not the entire member of the list.
I'd take an approach where I try to find and capture the duplicated keys and their values in a separate list and then merge in and remove the duplicates as a separate step.
The problem is that when you use the iItr.remove() it modifies the list, which iItr is happy with because it knows what changed, but oItr isn't. There are three possible solutions to this that I can see:
Switch to a concurrent list (e.g. ConcurrentLinkedQueue - but see then answers at Lock-Free Concurrent Linked List in Java for warnings about this)
Switch to a set structure, e.g. TreeSet, which will keep your items unique automatically (but won't preserve their order)
Make sure you don't use the other iterator after removing from one of them -- you could do this by switching which element you are removing, i.e. change iItr.remove() to:
oItr.remove();
break;
This would cause the first instance of each key to be removed, rather than subsequent ones, which might not be the behaviour you want -- in that case you could try iterating over the lists backwards.
Will this work?
Iterator<CElem> oItr = linkedList.iterator();
{
while (oItr.hasNext())
{
CElem outer = oItr.next();
Iterator<CElem> iItr = linkedList.iterator();
{
while (iItr.hasNext())
{
CElem inner = iItr.next();
if (outer.equals(inner))
continue;
if (outer.getKey().equals(inner.getKey()))
{
inner.getValues().addAll(outer.getValues());
outer.remove();
break;
}
}
}
}
}
The following code throws a ConcurrentModificationException:
for (String word : choices) {
List<String> choicesCopy = choices;
chosen.add(word);
choicesCopy.remove(word);
subsets(choicesCopy, chosen, alreadyPrinted);
}
What's going on? The original list (choices) isn't modified at all.
You made a reference copy not object copy in here
List<String> choicesCopy = choices;
So obviously you are modifying the same list and you are bound to get the ConcurrentModificationException
Use Collections.copy() to properly make a copy of your list.
EDIT:
As suggested below you can also use constructor for copying.
The reason is because you cannot modify anything inside a foreach loop. Try using a for loop. Or you have take all the contents of list and add them 1 at a time to the other list. because its done by reference
Edit: You need to make a deep copy of the list and remove from that copy. Then you can assign the reference of the original list to point to the new one that has the modifications. You cannot remove from the list you're currently iterating through even if it's being referenced by another variable.
Change the code like this:
for (Iterator<String> it = choices.iterator(); it.hasnext();) {
String word = it.next();
chosen.add(word);
it.remove();
subsets(choicesCopy, chosen, alreadyPrinted);
}
Explanation: foreach loops use an iterator internally, but don't expose it to the user. So if you want to remove items you have to simulate the foreach loop and keep a reference to the iterator yourself.
While iterating, any other means of removing data from a collection will result in a ConcurrentModificationException.
I think the universal solution is:
List<E> obj = Collections.synchronizedList(new ArrayList<E>());
You'll need to copy the list properly e.g. Collections.copy and then remove from the copy, or use Iterator.remove, which will remove the Object from the underlying collection. Iterators are fail fast, so you can't change the underlying Collection without using the API of the Iterator.
I suspect chosen should be a copy as well. Otherwise chosen will accumulates all the words by the time the loop has finished. i.e. I suspect the chosen and choices shouldn't have any words in common.
I also suspect the collections should be sets (unordered collections without duplicates) instead of lists.
Perhaps the code should be.
Set<String> choices =
Set<String> chosen =
for (String word : choices) {
Set<String> choicesCopy = new LinkedHashSet<String>(choices);
choicesCopy.remove(word);
Set<String> chosenCopy = new LinkedHashSet<String>(chosen);
chosenCopy.add(word);
subsets(choicesCopy, chosenCopy, alreadyPrinted);
}
I'm completely new to iterators. I have an ArrayList named "Test" with String objects. How would I go about using an iterator class? I've tried everything I can think of, it's just not making sense. Thanks for the help.
Say I have an Iterator named "iter". I need to step through my ArrayList in search of a certain String. When that String is found, I need to add it to a different ArrayList named "test2".
while(iter.hasNext()) {
if(iter.next() == sampleString) {
test2.add(sampleString);
}
}
Only problem with this, is that when I call next() it moves the pointer to the next String, ignoring the first String in the ArrayList. How would I implement this??
You don't need one. The ArrayList is already Iterable! :-D
ArrayList<String> test = new ArrayList<String>();
test.add("Hello");
test.add("world");
for(String str : test) System.out.println(str);
Iterators are generally used like this:
while (iter.hasNext()) {
String nextString = iter.next();
// Do something with the string...
}
Some (myself included) will prefer the enhanced for loop:
for (String nextString : listOfStrings) {
// Do something with the string
}
The for loop avoids the need for getting an explicit Iterator reference and includes the nextString variable declaration, keeping it concise and properly scoped.
The issue is that your not quite 100% on how Iterator.next() works
This is copied directly from the Java API
E next() -- Returns the next element in the iteration.
What this means is that .next() will return an object then move to the next item in the list.
You need to store the returned object when calling next()
while(iter.hasNext())
{
String temp = iter.next();
//this is a more old school method but more true to form.
//== doesn't necessarily do equality checks on all objects the way
//you would think
if(temp.equals(sampleString))
{
test2.add(temp);
}
}
An iterator is simply something that understands how to traverse a given data structure.
What is it that you aren't understanding?
For a List an iterator would keep track of its current position in the list and understand how to get the next element in the list. For something like a list, it's relatively simple, but you could also define an iterator for some other arbitrary data structure that isn't as simple as a list. You could also define one that does something differently like traverse the list backwards. If you have specific questions about iterators, update your question and we'll take a stab at it :-)
Iterator is a means of traversing a collection of items. Adhering to that statement, the java.util.Collection extends Iterable (public interface Collection<E> extends Iterable<E>). i.e all collection classes are Iterable in someway. iterator() is the method to get a handle to that Iterator. Once you have the handle you can traverse through the items.
I highlighted someway because not all Iterator's allow bi-directional traversal. ListIterator allows this.
I'd rewrite your code as below
for(String s : myCollection)
{
if(s.equals(sampleString))
{
test.add(s);
}
}
could not find anything on this, wondering if anyone knew about this or a possible workaround. I am using JDOM and working with an xml schema.
I have created a List of which are just xml tags. The algorithm's aim is to iterate through the List of elements and remove the element if a condition is met (in this case if it starts with a certain string). See below:
for (Element appinfo : appinfos) {
if (appinfo.getText().startsWith(
PARAMETER_DESCRIPTION_APPINFO)) {
removeAppInfoElement(appinfo, name, appinfo.getText());
}
}
However, the loop appears to be attempting to iterate to the element it just removed.
Does anyone see anything wrong with this? Do I need to abandon the enhanced for loop or dig deeper for cause of problem?
I suppose you're talking about ConcurrentModificationException. Try to use iterator instead.
Yes that wont work.
Add all the items you want to remove to a new collection and then do a removeAll with those elements on the original collection.
You cannot remove elements from a Collection directly as you iterate over it - this causes issues because the Iterator has no idea that the element has been removed.
Instead of the enhanced for-loop, use the Iterator directly and call the remove() function, for example:
for (Iterator it = appinfos.iterator(); it.hasNext();) {
Element appinfo : it.next();
if (someCondition) {
it.remove();
}
}
willcodejavaforfood's answer is one way of doing this.
An alternative, which may be better or worse depending on style and what else you want to do in the loop, is to get the Iterator explicitly and use its remove method:
final Iterator<Element> iter = appinfos.iterator();
while (iter.hasNext()) {
if (iter.next().getText().startsWith(
PARAMETER_DESCRIPTION_APPINFO)) {
iter.remove();
}
}
This of course only works if a simple removal from the collection is what you want to do. When invoking potentially complex methods that will directly remove from the underlying collection, the best approach is to take a copy of the collection initially, then iterate over this copy.
In all cases, modifying a collection while you are iterating over it will generally cause Bad Things to happen.