commons-issues mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Christophe Schmaltz (Jira)" <j...@apache.org>
Subject [jira] [Commented] (COLLECTIONS-663) Unexpected ConcurrentModificationException when altering Collection of a MultiValuedMap
Date Tue, 05 Nov 2019 20:37:00 GMT

    [ https://issues.apache.org/jira/browse/COLLECTIONS-663?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16967846#comment-16967846
] 

Christophe Schmaltz commented on COLLECTIONS-663:
-------------------------------------------------

Hi [~Guoping1] ,

thank you for looking into this. I worked around my problem since then.


Maybe you want to add in the documentation of {{MultiValuedMap.mapIterator()}} (or better
in it's implementation {{AbstractMultiValuedMap.mapIterator()}}) that it doesn't support {{setValue(Object)}},
as it's documented for asMap().

 

 

 

But I think the initial issue needs documentation even more:
{code:java}
	@Test
	public void test() {
		MultiValuedMap<Integer, Integer> multiMap = new HashSetValuedHashMap<>();
		multiMap.put(1, 10);
		multiMap.put(2, 20);
		for (Collection<Integer> innerCollection : multiMap.asMap().values()) {
			for (Iterator<Integer> iterator = innerCollection.iterator(); iterator.hasNext();)
{
				Integer i = iterator.next();
				iterator.remove(); // only the innerCollection is altered
			}
			// innerCollection.add(6); // adding stuff back should also work...
		}
	}{code}
This test unexpectedly throws a ConcurrentModificationException, which is a lot harder to
understand than the UnsupportedOperationException of the other test.

 

The {{ConcurrentModificationException}}  needs debugging to understand that it isn't the
user who removed concurrently, but the apache-commons library itself.

 

That {{AbstractMultiValuedMap.ValuesIterator.remove()}} is calling  {{AbstractMultiValuedMap.this.remove(key);}}
is incorrect if the user was using an iterator over the multimap:

 

 
{code:java}
 //  AbstractMultiValuedMap.ValuesIterator.remove(): 
       @Override
        public void remove() {
            iterator.remove();
            if (values.isEmpty()) { 
                // this is buggy if the user was iterating over the AbstractMultiValuedMap
                AbstractMultiValuedMap.this.remove(key);
            }
        }
{code}
Maybe MultiValuedMap.asMap() needs some documentation that it doesn't support {{remove()}}
in case of nested usage of {{iterator()}}.

Or find a way to call enclosingIterator.remove():
{code:java}
 //  AbstractMultiValuedMap.ValuesIterator.remove(): 
       @Override
        public void remove() {
            iterator.remove();
            if (values.isEmpty()) {
                if (hasEnclosingIterator()) {
                    enclosingIterator.remove(); // nested iteration
                } else {
                    AbstractMultiValuedMap.this.remove(key); // non nested usage
                }
            }
        }
{code}
 hasEnclosingIterator() and enclosingIterator have to be implemented, somehow :D

 

> Unexpected ConcurrentModificationException when altering Collection of a MultiValuedMap
> ---------------------------------------------------------------------------------------
>
>                 Key: COLLECTIONS-663
>                 URL: https://issues.apache.org/jira/browse/COLLECTIONS-663
>             Project: Commons Collections
>          Issue Type: Bug
>            Reporter: Christophe Schmaltz
>            Assignee: Bruno P. Kinoshita
>            Priority: Trivial
>
> Testcase:
> {code}	@Test
> 	public void test() {
> 		MultiValuedMap<Integer, Integer> multiMap = new HashSetValuedHashMap<>();
> 		multiMap.put(1, 10);
> 		multiMap.put(2, 20);
> 		for (Collection<Integer> innerCollection : multiMap.asMap().values()) {
> 			for (Iterator<Integer> iterator = innerCollection.iterator(); iterator.hasNext();)
{
> 				Integer i = iterator.next();
> 				iterator.remove(); // only the innerCollection is altered
> 			}
> 			// innerCollection.add(6); // adding stuff back should also work...
> 		}
> 	}{code}
> This test unexpectedly throws a ConcurrentModificationException.
> The issue is that when calling {{iterator.remove()}} the {{AbstractMultiValuedMap.ValuesIterator}}
detects that the Collection is empty and calls {{AbstractMultiValuedMap.this.remove(key);}}.
> It may be better if the iterator of the inner collection had a reference on the iterator
if the outer map and called {{containerIterator.remove()}} instead.
> *Note:* this solution would again present issues if the user tries to add new elements
in this now empty collection (which was removed from the parent).
> In the current state, it is quite unclear why an exception is thrown, without debugging
the code. 



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Mime
View raw message