Hi Brent,

On the Map.compute* methods.

Perhaps we can reuse similar language to that we added for Matcher:

  The mapping function should not modify this map during computation. This 
method will, on a best-effort basis, throw a
  ConcurrentModification if such modification is detected. 

It's tempting to place the second sentence on the @throws 
ConcurrentModificationException as i believe will not show up for methods on 
ConcurrentMap, so keeps it somewhat contained documentation wise.



On Mar 6, 2015, at 8:08 PM, Brent Christian <brent.christ...@oracle.com> wrote:

> Hi.  I'm picking this back up now.
> 
> In Map/HashMap, there are already two methods that accept lambdas and throw a 
> CME:
>  forEach(BiConsumer)
>  replaceAll(BiFunction)
> 
> The Map interface documents these methods to state that "Exceptions are 
> relayed to the caller", and has an @throws CME tag "if an entry is found to 
> be removed".  (The default code does not throw a CME if items are added).
> 

Right, although the iterator might still be fail-fast.


> HashMap adds no additional documentation on these methods.  It has code to 
> check the modCount, and throws a CME if it changes.
> 
> 
> The Map/HashMap methods I would like to update with this fix are:
>  compute(Key, BiFunction)
>  computeIfAbsent(Key, Function)
>  computeIfPresent(Key, BiFunction)
>  merge(Key, Value, BiFunction)
> 
> I would like to update the docs & code as follows:
> Map:
>    Docs: discourage modifying the map from function code; encourage 
> implementing classes to detect/throw CME
>    Code: no change
> 
> HashMap/Hashtable:
>    Docs: document that structural modifications will result in a CME; add 
> @throws CME
>    Code: check modCount & throw CME
> 
> ConcurrentMap:
>    Docs: discourage modifying the map from function code
>    Code: no change
> 
> 
> My first draft for the docs change, on computeIfAbsent():
> 
> diff -r 330dcd651f3b src/java.base/share/classes/java/util/Map.java
> --- a/src/java.base/share/classes/java/util/Map.java    Mon Feb 02 12:35:18 
> 2015 -0800
> +++ b/src/java.base/share/classes/java/util/Map.java    Fri Feb 06 12:49:19 
> 2015 -0800
> @@ -925,6 +925,11 @@
>      * }
>      * }</pre>
>      *
> +     * <p>The mappingFunction itself should not make changes to this map.
> +     * Implementing classes are encouraged to detect such modifications and
> +     * throw ConcurrentModificationException. The default implementation does
> +     * not do so.
> +     *
>      * <p>The default implementation makes no guarantees about synchronization
>      * or atomicity properties of this method. Any implementation providing
>      * atomicity guarantees must override this method and document its
> 
> diff -r 330dcd651f3b src/java.base/share/classes/java/util/HashMap.java
> --- a/src/java.base/share/classes/java/util/HashMap.java        Mon Feb 02 
> 12:35:18 2015 -0800
> +++ b/src/java.base/share/classes/java/util/HashMap.java        Fri Feb 06 
> 12:49:19 2015 -0800
> @@ -1082,6 +1082,17 @@
>         return null;
>     }
> 
> +    /**
> +     * {@inheritDoc}
> +     *
> +     * <p>The mappingFunction itself should not make changes to this map.
> +     * If the function causes a structural modification to the map, a
> +     * ConcurrentModificationException will be thrown.  As with iterators, 
> this
> +     * exception is thrown on a best-effort basis.
> +     *
> +     * @throws ConcurrentModificationException if a structural change was
> +     * detected while executing the mappingFunction
> +     */
>     @Override
>     public V computeIfAbsent(K key,
>                              Function<? super K, ? extends V> 
> mappingFunction) {
> 
> diff -r 330dcd651f3b 
> src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java
> --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java   
> Mon Feb 02 12:35:18 2015 -0800
> +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java   
> Fri Feb 06 12:49:19 2015 -0800
> @@ -305,6 +305,8 @@
>      * threads attempt updates including potentially calling the mapping
>      * function multiple times.
>      *
> +     * <p>The mappingFunction itself should not make changes to this map.
> +     *
>      * <p>This implementation assumes that the ConcurrentMap cannot contain 
> null
>      * values and {@code get()} returning null unambiguously means the key is
> ---
> 
> If that looks okay, I will apply it to the other methods.
> 
> 
> I came across a few methods used by the list classes that I wanted to point 
> out:
> 
> forEach(Consumer) on the Iterable interface
> removeIf(Predicate) on the Collection interface
> replaceAll(UnaryOperator) on the List interface
> 
> They all document that exceptions are relayed to the caller.  They do not 
> have a @throws CME tag.  The default code uses iterators (or enhanced for()). 
>   LinkedList uses the default versions.  ArrayList overrides the methods, 
> adds no docs of its own, and has code to check the modCount and throw CME.
> 
> Would an "@throws CME" improve the JavaDoc of these default methods? I'm 
> leaning toward, "not really."  

Same here, they require traversal so use an iterator or an internal equivalent, 
thus the CME pops out due to that.

Paul.

> Between the clause about exceptions being relayed to the caller, and knowing 
> that the default method is using iterators (mentioned in the JavaDoc), and 
> that iterators will throw CMEs, one can deduce that modifying the list from 
> the lambda will result in a CME.  It's not clearly spelled out, but then, 
> modifying the collection this way falls outside the intended usage of these 
> methods.
> 
> Thanks for any additional feedback.
> 
> -Brent
> 
> On 2/6/15 1:12 PM, Brent Christian wrote:
>> diff -r 330dcd651f3b src/java.base/share/classes/java/util/Map.java
>> --- a/src/java.base/share/classes/java/util/Map.java    Mon Feb 02
>> 12:35:18 2015 -0800
>> +++ b/src/java.base/share/classes/java/util/Map.java    Fri Feb 06
>> 12:49:19 2015 -0800
>> @@ -925,6 +925,11 @@
>>       * }
>>       * }</pre>
>>       *
>> +     * <p>The mappingFunction itself should not make changes to this map.
>> +     * Implementing classes are encouraged to detect such modifications
>> and
>> +     * throw ConcurrentModificationException. The default
>> implementation does
>> +     * not do so.
>> +     *
>>       * <p>The default implementation makes no guarantees about
>> synchronization
>>       * or atomicity properties of this method. Any implementation
>> providing
>>       * atomicity guarantees must override this method and document its
>> 
>> diff -r 330dcd651f3b src/java.base/share/classes/java/util/HashMap.java
>> --- a/src/java.base/share/classes/java/util/HashMap.java        Mon Feb
>> 02 12:35:18 2015 -0800
>> +++ b/src/java.base/share/classes/java/util/HashMap.java        Fri Feb
>> 06 12:49:19 2015 -0800
>> @@ -1082,6 +1082,17 @@
>>          return null;
>>      }
>> 
>> +    /**
>> +     * {@inheritDoc}
>> +     *
>> +     * <p>The mappingFunction itself should not make changes to this map.
>> +     * If the function causes a structural modification to the map, a
>> +     * ConcurrentModificationException will be thrown.  As with
>> iterators, this
>> +     * exception is thrown on a best-effort basis.
>> +     *
>> +     * @throws ConcurrentModificationException if a structural change was
>> +     * detected while executing the mappingFunction
>> +     */
>>      @Override
>>      public V computeIfAbsent(K key,
>>                               Function<? super K, ? extends V>
>> mappingFunction) {
>> 
>> diff -r 330dcd651f3b
>> src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java
>> ---
>> a/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java
>>     Mon Feb 02 12:35:18 2015 -0800
>> +++
>> b/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java
>>     Fri Feb 06 12:49:19 2015 -0800
>> @@ -305,6 +305,8 @@
>>       * threads attempt updates including potentially calling the mapping
>>       * function multiple times.
>>       *
>> +     * <p>The mappingFunction itself should not make changes to this map.
>> +     *
>>       * <p>This implementation assumes that the ConcurrentMap cannot
>> contain null
>>       * values and {@code get()} returning null unambiguously means the
>> key is

Reply via email to