See https://bugs.openjdk.java.net/browse/JDK-8029676

Stephen

On 26 November 2013 13:20, Stephen Colebourne <scolebou...@joda.org> wrote:
> I took a quick look, but jumped back in horror at the start of the
> Javadoc for the new methods in Map. A Javadoc description should start
> with the positive, not the negative. I had to read the code to figure
> out what they are supposed to do. Here are the four poor Javadocs and
> some proposed enhacements:
>
> merge()
> "If the specified key is not already associated with ..."
> should be
> "Updates the value for an existing key merging the existing value with
> the specified value."
> "<p>"
> "....more detailed user-level explanation..."
> "<p>"
> "....now the more spec/edge case part..."
> ("if" is a terrible way to start the docs. Say what it does!)
>
>
> computeIfAbsent()
> "If the specified key is not already associated with a value (or is mapped..."
> should be
> "Associates the key with a value computed on demand if the key is not
> currently present."
> "<p>"
> "....more detailed user-level explanation..."
> "<p>"
> "....now the more spec/edge case part..."
>
>
> computeIfPresent()
> "If the value for the specified key is present and non-null,"
> should be
> "Updates the value for an existing key."
> "<p>"
> "....more detailed user-level explanation..."
> "<p>"
> "....now the more spec/edge case part..."
>
>
> compute()
> "Attempts to compute a mapping for the specified key..."
> should be
> "Updates the value for a key, adding a new mapping if necessary."
> "<p>"
> "....more detailed user-level explanation..."
> "<p>"
> "....now the more spec/edge case part..."
> (attempts is negative, not positive)
>
> Similar problems seem to exist for "putIfAbsent".
>
> also...
>
> I have to say that I don't overly like the method name.
> "map.merge(...)" sounds like a bulk operation affecting the whole map
> (ie. like putAll). Assuming that the method cannot be renamed, it
> could be improved just by changing the method parameter names.:
>
> default V merge(K key, V valueToMerge,
>     BiFunction<? super V, ? super V, ? extends V> mergeFunction) {
>
> "valueToMerge" implies that action will occur to the parameter.
> "mergeFunction" is much more meaningful that "remapping Function".
>
> Similar parameter name change, "mappingFunction" -> "valueCreationFunction"
> default V computeIfAbsent(
>   K key,
>   Function<? super K, ? extends V> valueCreationFunction) {
>
> Similar parameter name change, "remappingFunction" -> "valueUpdateFunction"
> default V computeIfPresent(
>   K key,
>   BiFunction<? super K, ? super V, ? extends V> valueUpdateFunction) {
>
> Similar parameter name change, "remappingFunction" -> "valueUpdateFunction"
> default V compute(
>   K key,
>   BiFunction<? super K, ? super V, ? extends V> valueUpdateFunction) {
>
>
> also...
> Can you use HashSet::new in the example code?
>
>
> In general, all these new methods are written in a spec style, rather
> than a user friendly style, and I'm afraid I don't think thats
> sufficient.
> Stephen
>
>
>
> On 26 November 2013 04:32, Mike Duigou <mike.dui...@oracle.com> wrote:
>>
>> On Nov 24 2013, at 16:31 , David Holmes <david.hol...@oracle.com> wrote:
>>
>>> Hi Mike,
>>>
>>> There is still no clear spec for what should happen if the param value is 
>>> null.
>>
>> I feel very uncomfortable the status quo of with null being ignored, used 
>> for a sentinel and also as value. The relations between null and values in 
>> this method are just too complicated.
>>
>> Currently:
>>
>> - The provided value may be either null or non-null. Is null a legal value? 
>> It depends upon:
>>         - Is there an existing value?
>>         - Does the Map allow null values?
>>         - Does the function allow null values?
>> - Existing null values are treated as absent.
>>         - If a null value is passed should we remove this mapping or add it 
>> to the map?
>>               - null might not be accepted by the map
>>               - The associated value would still be regarded as absent for 
>> future operations.
>> - The function may return null to signal "remove".
>>
>> In particular I dislike adding a null entry to the map if there is no 
>> current mapping (or mapped to null). It seems like it should be invariant 
>> whether we end up with an associated value. If null is used to signify 
>> "remove" then map.contains(key) will return variable results depending upon 
>> the initial state. Having the behaviour vary based upon whether the map 
>> allows null values would be even worse.
>>
>> So I would like to suggest that we require value to be non-null. I have 
>> provisionally updated the spec and impls accordingly.
>>
>>> The parenthesized part is wrong.
>>
>> I think that's overzealous copying from compute(). I have removed it.
>>
>>>
>>> Also you have changed the method implementation not just the implDoc so the 
>>> bug synopsis is somewhat misleading.
>>
>> I will correct this. More changes were made than I originally expected. New 
>> synopsis will be "Map.merge implementations should refuse null value param"
>>
>> I have updated the webrev.
>>
>> http://cr.openjdk.java.net/~mduigou/JDK-8029055/1/webrev/
>>
>> Thanks,
>>
>> Mike
>>
>>>
>>> Thanks,
>>> David
>>>
>>> On 23/11/2013 10:25 AM, Mike Duigou wrote:
>>>> We'll be using https://bugs.openjdk.java.net/browse/JDK-8029055 for this 
>>>> issue.
>>>>
>>>> I've posted a webrev here:
>>>>
>>>> http://cr.openjdk.java.net/~mduigou/JDK-8029055/0/webrev/
>>>>
>>>> There is an identical change in ConcurrentMap's merge().
>>>>
>>>> Mike
>>>>
>>>> On Nov 22 2013, at 16:01 , Henry Jen <henry....@oracle.com> wrote:
>>>>
>>>>>
>>>>> On 11/21/2013 06:33 PM, David Holmes wrote:
>>>>>> On 22/11/2013 5:02 AM, Louis Wasserman wrote:
>>>>>>> While I agree that case should be specified, I'm not certain I follow 
>>>>>>> why
>>>>>>> that's what's going on here.
>>>>>>>
>>>>>>> The weird condition is that if oldValue is null, not value; oldValue
>>>>>>> is the
>>>>>>> old result of map.get(key).  The Javadoc seems not just unspecified, but
>>>>>>> actively wrong.  Here's a worked example:
>>>>>>>
>>>>>>> Map<String, Integer> map = new HashMap<>();
>>>>>>> map.merge("foo", 3, Integer::plus);
>>>>>>> Integer fooValue = map.get("foo");
>>>>>>>
>>>>>>> Going through the Javadoc-specified default implementation:
>>>>>>>
>>>>>>>   V oldValue = map.get(key); // oldValue is null
>>>>>>>   V newValue = (oldValue == null) ? value :
>>>>>>>                remappingFunction.apply(oldValue, value);
>>>>>>>      // newValue is set to value, which is 3
>>>>>>>   if (newValue == null) // newValue is nonnull, branch not taken
>>>>>>>       map.remove(key);
>>>>>>>   else if (oldValue == null) // oldValue is null, branch taken
>>>>>>>       map.remove(key); // removes the entry from the map
>>>>>>>   else // else if was already triggered, branch not taken
>>>>>>>       map.put(key, newValue);
>>>>>>>
>>>>>
>>>>> Seems like a document bug to me, we should fix this @implSpec.
>>>>>
>>>>> Cheers,
>>>>> Henry
>>>>>
>>>>
>>

Reply via email to