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

Alex Herbert commented on COLLECTIONS-803:
------------------------------------------

A cast to (K) may throw a cast exception as convertKey can return a plain 
Object in convertKey (which is the mask object AbstractHashedMap.NULL). So it 
is not possible to override the existing API with the converted key. Thanks for 
identifying this as I had not tried out that idea.

I think this can be solved using a cache of the converted key, e.g.
{code:java}
    private Object[] convertedKey = new Object[2];

    // ...

    @Override
    protected Object convertKey(final Object key) {
        if (key != null) {
            // Copy locally
            final Object[] pair = convertedKey;
            if (pair[0] == key) {
                return pair[1];
            }
            final char[] chars = key.toString().toCharArray();
            for (int i = chars.length - 1; i >= 0; i--) {
                chars[i] = 
Character.toLowerCase(Character.toUpperCase(chars[i]));
            }
            String converted = new String(chars);
            // Save for next conversion
            convertedKey = new Object[] {key, converted};
            return converted;
        }
        return AbstractHashedMap.NULL;
    }
{code}
The map is not thread safe. So there is no requirement here for an atomic 
reference. If there is any sort of memory issue where the convertedKey does not 
match as expected (inside the createEntry method which converts the key again) 
then the method will simply compute it again. Overhead over the existing 
implementation is a single brach where the key may match and the converted form 
is returned immediately. The rest is just simple housekeeping.

I've not tested this. It will require some checking that the custom 
serialization methods function correctly. Adding a new private field changes 
the serialization compatibility. The key point is that deserialization of a 
serialized form saved with a prior version should end up with a non-null 
convertedKey array. It doesn't matter what the array contains, just that it is 
not null, and length at least 1, or else a NPE or IOOBE will occur on the first 
put operation.

 

> CaseInsensitiveMap prevent duplicate key conversion on put
> ----------------------------------------------------------
>
>                 Key: COLLECTIONS-803
>                 URL: https://issues.apache.org/jira/browse/COLLECTIONS-803
>             Project: Commons Collections
>          Issue Type: Improvement
>          Components: Map
>    Affects Versions: 4.4
>            Reporter: Simulant
>            Priority: Minor
>              Labels: performance
>          Time Spent: 1h 50m
>  Remaining Estimate: 0h
>
> When adding a new item into a {{CaseInsensitiveMap}} the {{convertKey(key)}} 
> method is called twice, once in the {{put(key, value)}} method and second in 
> the {{createEntry(next, hashCode, key, value)}} method. The result could be 
> re-used resulting in a better performance.
> Depending on the {{toString()}} implementation of the key and the resulting 
> length of the key before the lower case conversion the operation can get 
> expensive and should not be called twice, as the {{CaseInsensitiveMap}} 
> overwrites the {{convertKey(key)}} method and makes it more expensive and 
> depending on the input unlike in the implementation of the 
> {{AbstractHashedMap}}.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to