How does Java delete elements during List or Map traversal
- 2020-05-19 04:53:32
- OfStack
There are many ways to iterate through and remove elements from List or Map, which can cause problems when used incorrectly. Learn again with this article.
1. Delete elements during List traversal
Traversal using index subscripts
Example: delete 2 from the list
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(2);
list.add(3);
list.add(4);
for (int i = 0; i < list.size(); i++) {
if(2 == list.get(i)){
list.remove(i);
}
System.out.println(list.get(i));
}
System.out.println("list=" + list.toString());
}
Output results:
1
2
3
4
list=[1, 2, 3, 4]
Question:
The result shows that only one 2 was deleted, and the other 2 was omitted. The reason is that after the first 2 was deleted, the number of elements in the set was reduced by 1, and the subsequent elements were moved forward by 1 bit, resulting in the omission of the second 2.
Use For for loop traversal
Example:
public static void listIterator2(){
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(2);
list.add(3);
list.add(4);
for (int value : list) {
if(2 == value){
list.remove(value);
}
System.out.println(value);
}
System.out.println("list=" + list.toString());
}
Results:
Exception in thread "main" 1
2
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at test.ListIterator.listIterator2(ListIterator.java:39)
at test.ListIterator.main(ListIterator.java:10)
Description:
Description of ConcurrentModificationException in jdk:
public class ConcurrentModificationException extends
RuntimeException throws this exception when a method detects concurrent changes to an object but does not allow such changes.
For example, when a thread iterates over an Collection, another linear modification of that Collection is usually not allowed. Often in these cases, the outcome of an iteration is 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 quick-fail iterator, because the iterator fails completely quickly, without the risk of arbitrary uncertain behavior at some point in the future.
Note: this exception does not always indicate that the object has been modified concurrently by different threads. If a single thread issues a sequence of method calls that violates an object's protocol, the object may throw this exception. For example, if the thread directly modifies the collection while iterating over the collection using a quick-fail iterator, the iterator throws this exception.
Note: the quick failure behavior of an iterator cannot be guaranteed, because 1 in general, it is not possible to make any hard and fast guarantees about asynchronous concurrent changes. A quick fail operation is thrown as best it can
ConcurrentModificationException
. Therefore, it is a mistake to write a program that relies on this exception to improve the correctness of such an operation. The correct approach is:
ConcurrentModificationException
It should only be used to detect bug.
For each in Java actually USES iterator for processing. While iterator does not allow collections to be deleted during the use of iterator. So that causes iterator to throw
ConcurrentModificationException
.
The right way
Example:
public static void listIterator3(){
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(2);
list.add(3);
list.add(4);
Iterator<Integer> it = list.iterator();
while (it.hasNext()){
Integer value = it.next();
if (2 == value) {
it.remove();
}
System.out.println(value);
}
System.out.println("list=" + list.toString());
}
Results:
1
2
2
3
4
list=[1, 3, 4]
2. Delete elements during Map traversal
Examples of the right approach:
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("1", "test1");
map.put("2", "test2");
map.put("3", "test3");
map.put("4", "test4");
// Complete traverse Map
for (Entry<String, String> entry : map.entrySet()) {
System.out.printf("key: %s value:%s\r\n", entry.getKey(), entry.getValue());
}
// Remove elements
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while(it.hasNext())
{
Map.Entry<String, String> entry= it.next();
String key= entry.getKey();
int k = Integer.parseInt(key);
if(k%2==1)
{
System.out.printf("delete key:%s value:%s\r\n", key, entry.getValue());
it.remove();
}
}
// Complete traverse Map
for (Entry<String, String> entry : map.entrySet()) {
System.out.printf("key: %s value:%s\r\n", entry.getKey(), entry.getValue());
}
}
Results:
key: 1 value:test1
key: 2 value:test2
key: 3 value:test3
key: 4 value:test4
delete key:1 value:test1
delete key:3 value:test3
key: 2 value:test2
key: 4 value:test4
Pay attention to
But for iterator
remove()
Methods, we also need to pay attention to:
Once per call
iterator.next()
Method, which can only be called once
remove()
Methods.
call
remove()
Method must be called once before
next()
Methods.
Description of the remove() method in es1064en-API:
void remove()
Removes 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 to next. If the collection that the iterator points to is modified in a way other than by calling this method when iterating, the iterator's behavior is not clear.
Throws:
ConcurrentModificationException
0
- if the iterator is not supported
remove
Operation.
IllegalStateException
- if not already called
next
Method, or called the last time
next
Method has been called since
remove
Methods.
conclusion
The above is all about List and Map to delete the elements in the process of traversing, I hope the content of this article can bring you a certain help in your study or work, if you have any questions, you can leave a message to communicate.