question
I need to remove unspecified elements from the first collection from a collection of java, based on the content of another collection. This looks very simple, but it's a problem.
This is the head of the method I want to write
private void screenBlackNameList(List<SharedBoardSmsWrapper> source, List<BlackNameListModel> blackNameList)
This is how things are. The source collection saves some display data elements. The blackNameList collection saves a blacklist list. We need to remove the data of blacklisted users in the source collection based on the blacklist table.
The solution to this problem seems very simple.
I first use the for each statement to delete.
for(SharedBoardSmsWrapper tmpSharedBoardSmsWrapper:source){ for(BlackNameListModel tmpBlackNameListModel:blackNameList){ if(tmpSharedBoardSmsWrapper.getSource().equals(tmpBlackNameListModel.getSource())){ source.remove(tmpSharedBoardSmsWrapper); break; } } }Very simple question! I laughed secretly,
test…
What surprised me was that this code actually threw an exception
java.util.ConcurrentModificationException。
View JDK6 manual
public class ConcurrentModificationExceptionextends RuntimeException
This exception is thrown when the method detects concurrent modification of the object but does not allow such modification.
For example, when a thread iterates on a Collection , another collection is not allowed to be linearly modified. Usually in these cases, the results of the iteration are uncertain. If this behavior is detected, some iterator implementations (including all common collection implementations provided by JRE) may choose to throw this exception. The iterator that performs this operation is called a fast failure iterator because the iterator fails very quickly without risking the risk of arbitrary uncertain behavior at some time in the future.
Note that this exception does not always indicate that the object has been modified concurrently by a different thread. If a single thread issues a sequence of method calls that violates the object's contract, the object may throw this exception. For example, if a thread directly modifies the collection when iterates on the collection using a fast fail iterator, the iterator will throw this exception.
Note that the fast failure behavior of the iterator cannot be guaranteed, because generally, it is impossible to make any hard guarantees on whether there is an out-of-synchronous concurrent modification. A quick failure operation will do its best to throw ConcurrentModificationException . Therefore, it is wrong to write a program that depends on this exception to improve the correctness of such operations. The correct way is : ConcurrentModificationException should be used only to detect bugs.
For each in Java actually uses iterator for processing. Iterator does not allow collections to be deleted during the use of iterator . And when I was in for each , I deleted an element from the collection, which caused the iterator to throw ConcurrentModificationException .
It seems that we can only use the traditional for loop honestly!
for(int i=0;i<source.size();i++){ SharedBoardSmsWrapper tmpSharedBoardSmsWrapper=source.get(i); for(int j=0;j<blackNameList.size();j++){ BlackNameListModel tmpBlackNameListModel=blackNameList.get(j); if(tmpSharedBoardSmsWrapper.getSource().equals(tmpBlackNameListModel.getSource())){ source.remove(tmpSharedBoardSmsWrapper); break; } } }It should be fine now! Press the test with confidence...
faint! What's going on? How can the data be filtered wrong?
After Debug tracking, it was found that when the set deletes elements, the size of the set will become smaller and the index will change!
What should I do? I won't be helpless by such a small problem!
Use Iterator to delete elements in collection
Check the Iterator interface of the JDK manual and see that it also has a remove method.
Remove
void remove()
Remove the last element returned by the iterator from the collection pointed to by the iterator (optional operation). This method can only be called once per call next. If the iterator is modified by a collection pointing to by the iterator using a method other than calling this method, the iterator's behavior is uncertain.
Throw:
UnsupportedOperationException - If the iterator does not support remove operation.
IllegalStateException - If the next method has not been called, or the remove method has been called after the last call to the next method.
Correct final code:
/** *@paramsource *@paramblackNameList */ privatevoid screenBlackNameList(List<SharedBoardSmsWrapper> source, List<BlackNameListModel> blackNameList){ Iterator<SharedBoardSmsWrapper> sourceIt=source.iterator(); while(sourceIt.hasNext()){ SharedBoardSmsWrapper tmpSharedBoardSmsWrapper=sourceIt.next(); Iterator<BlackNameListModel> blackNameListIt=blackNameList.iterator(); while(blackNameListIt.hasNext()){ BlackNameListModel tmpBlackNameListModel=blackNameListIt.next(); if(tmpSharedBoardSmsWrapper.getSource().equals(tmpBlackNameListModel.getSource())){ sourceIt.remove(); break; } } } } } Note that the next() remove() of Iterator cannot be called multiple times. Otherwise, an exception will be thrown.
It seems that the easiest way to delete elements in a collection is to use Iterator 's remove() method!
Let's see how the Iterator provided by the ArrayList class is implemented.
privateclass Itr implements Iterator<E> { /**This is the index of the element, which is equivalent to a pointer, or cursor, which uses it to access the List's data element. *Indexofelementtobereturnedbysubsequentcalltonext. */ intcursor = 0; /** *Indexofelementreturnedbymostrecentcalltonextor *previous. Resetto-1ifthiselementisdeletedbyacall *toremove.最新元素的索引。 If the element has been deleted, set to -1 */ intlastRet = -1; /**Properties of the external class ArrayList: protected transient int modCount = 0; It is used to observe whether the ArrayList is being modified by other threads at the same time. If it is inconsistent, a synchronous exception will be thrown. *ThemodCountvaluethattheiteratorbelievesthatthebacking *Listshouldhave. If this expectationisviolated,theiterator *hasdetectedconcurrentmodification. */intexpectedModCount = modCount;//If the cursor does not reach the size of the List, then there are still elements. publicboolean hasNext() { returncursor != size(); }//Return the current element and then cursor +1. Recent index = index of returned elements. public E next() { checkForComodification(); try { E next = get(cursor); lastRet = cursor++; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); thrownew NoSuchElementException(); } }/*Delete an element, which means deleting the current element and putting cursor-1. Because, List will move all the following elements into the previous one. */ publicvoid remove() { if (lastRet == -1) thrownew IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { thrownew ConcurrentModificationException(); } } finalvoid checkForComodification() { if (modCount != expectedModCount) thrownew ConcurrentModificationException(); } }Summarize
As you can see, Iterator deletes the element and resets the cursor to the correct seat. As long as no other threads change the set at the same time, there will be no problem. The above is all about this article, I hope it will be helpful to everyone to learn Java.