...
This noncompliant code example (based on Sun Developer Network SDN 2011 bug report 6687277) uses the ArrayList
's remove()
method to remove an element from an ArrayList
while iterating over the ArrayList
. The resulting behavior is unspecified.
Code Block | ||
---|---|---|
| ||
class BadIterate {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
Iterator iter = list.iterator();
while (iter.hasNext()) {
String s = (String)iter.next();
if (s.equals("one")) {
list.remove(s);
}
}
}
}
|
...
The Iterator.remove()
method removes the last element returned by the iterator from the underlying Collection
. Its behavior is fully specified, so it may be safely invoked while iterating over a collection.
Code Block | ||
---|---|---|
| ||
// ...
if (s.equals("one")) {
iter.remove();
}
// ...
|
...
Although acceptable in a single-threaded environment, this noncompliant code example is insecure in a multithreaded environment because it is possible for another thread to modify the widgetList
while the current thread iterates over the widgetList
. Additionally, the doSomething()
method could modify the collection during iteration.
Code Block | ||
---|---|---|
| ||
List<Widget> widgetList = new ArrayList<Widget>();
public void widgetOperation() {
// May throw ConcurrentModificationException
for (Widget w : widgetList) {
doSomething(w);
}
}
|
...
This compliant solution wraps the ArrayList
in a synchronized collection so that all modifications are subject to the locking mechanism.
Code Block | ||
---|---|---|
| ||
List<Widget> widgetList = Collections.synchronizedList(new ArrayList<Widget>()); public void widgetOperation() { synchronized (widgetList) { // Client-side locking for (Widget w : widgetList) { doSomething(w); } } } |
This approach must be implemented correctly to avoid starvation, deadlock, and scalability issues [Goetz 2006a].
...
This compliant solution creates a deep copy of the mutable widgetList
before iterating over it.
Code Block | ||
---|---|---|
| ||
List<Widget> widgetList = new ArrayList<Widget>();
public void widgetOperation() {
List<Widget> deepCopy = new ArrayList<Widget>();
synchronized (widgetList) { // Client-side locking
for (Object obj : widgetList) {
deepCopy.add(obj.clone());
}
}
for (Widget w : deepCopy) {
doSomething(w);
}
}
|
...
The CopyOnWriteArrayList
data structure implements all mutating operations by making a fresh copy of the underlying array. It is fully thread-safe and is optimized for cases where traversal operations vastly outnumber mutations. Note that traversals of such lists always see the list in the state it had at the creation of the iterator (or enhanced for loop); subsequent modifications of the list are invisible to an ongoing traversal. Consequently, this solution is inappropriate when mutations of the list are frequent or when new values should be reflected in ongoing traversals.
Code Block | ||
---|---|---|
| ||
List<Widget> widgetList = new CopyOnWriteArrayList<Widget>();
public void widgetOperation() {
for (Widget w : widgetList) {
doSomething(w);
}
}
|
...