JDK-6821196 : CopyOnWriteArrayList/Set should provide snapshot methods
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 6u10
  • Priority: P5
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2009-03-24
  • Updated: 2016-02-10
Related Reports
Relates :  
Description
A DESCRIPTION OF THE REQUEST :
The CopyOnWrite collections are very useful, however they are difficult to use if your code logic needs to make more than one call on the collection within a logical operation because the contents of the collection could change between each call.  These collections should provide "snapshot()" methods which return cheap, unmodifiable copies which will never be altered by changes to the original collection.  Thus, code which needs to make more than one call on a collection could snapshot the collection before proceeding.  A snapshot could also be safely passed to other code as it would be unmodifiable.

JUSTIFICATION :
There is no way to really read a CopyOnWrite collection other than iterating over it, which dramatically limits the usefulness of these collections.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I would like to see a cheap implementation of the "getSnapshot()" method in the test case below, such that the output of the given code was always consistent with the logical branches in the if statements.

Granted, the "getSnapshot()" method could be implemented by copying the current contents of _list to a new ArrayList, but the underlying array should not need to be copied.  There should be a cheap method which returns, essentially, "Collections.unmodifableList(Arrays.asList(_list.array))".
ACTUAL -
If the list is modified while the doSomething() method is executing, the output will not be consistent with the logical decisions made (in some cases, an exception could be thrown).

---------- BEGIN SOURCE ----------
public class MyClass {
  public final CopyOnWriteArrayList<String> _list = new CopyOnWriteArrayList<String>();

  private List<String> getSnapshot() {
    return _list;
  }

  public void doSomething() {
    List<String> snap = getSnapshot();
    if(snap.size() == 1) {
      System.out.println("My list has one element: " + snap.get(0));
    } else {
      System.out.println("My list does not have one element: " + snap);
    }
  }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
return "new ArrayList<String>(_list)" from getSnapshot(), which is way more inneficient than it needs to be.

Comments
Another obvious approach is adding methods from Deque, notably descendingIterator(). Maybe we can even implement the Deque interface?
13-08-2015

For my particular problem, I want to iterate over the list in reverse. As I iterate, if there is something I don't like, then I will remove it. So, the unmodifiable snapshot() isn't going to help. However, for the original problem which created this issue, the unmodifiable snapshot() would allow for iterating over the elements via the index... for (int i = 0; i < snap.size(); i++) snap.get(i)...
13-08-2015

Martin B mentioned another possibility, which is to have a snapshot() method that returns an unmodifiable view. This has different semantics from clone(), which returns a modifiable snapshot. An unmodifiable snapshot seems safer if the use case is to iterate the list, find the last element, etc. in a consistent and safe fashion, without modifying the list.
13-08-2015

The solution seems to be to use clone(). See http://cs.oswego.edu/pipermail/concurrency-interest/2009-March/005926.html and follow the messages several threads down. Is it worth changing clone() to have a covariant override, and to update its doc to make cases like this more clear?
05-08-2015

The submitter should follow-up on concurrency-interest to get discussion going and gauge interest/usefulness.
04-04-2013