1

I tried to extend the HashMap class to implement a custom remove() method, like this:

@SuppressWarnings("serial")
private final class EntryHashMap<K, E> extends HashMap<K, E> {

    @Override
    public E remove(Object key) {
        rowKeyMap.remove(key);
        colKeyMap.remove(key);
        return super.remove(key);
    }

}

It all works well if remove() is invoked directly, but when an iterator is used to remove the entry, the remove() method of HashMap is invoked instead of the above overriden remove() method:

public boolean selectiveRemove(Object key) {
    boolean result = false;
    Iterator<Entry<K, E>> it = entrySet().iterator();
    while( it.hasNext() ) {
        Entry<K, E> entry = it.next();
        K rowKey = entry.getKey();
        if( Utils.equals(key, rowKey) ) {
            it.remove(); // <<<<< this does not invoke the new `remove()`
            result = true;
        }
    }
    return result;
}

After looking at the source code for HashMap in the entry iterator I see:

private abstract class HashIterator {
    . . .

    public boolean hasNext() {
        . . .
    }

    HashMapEntry<K, V> nextEntry() {
        . . .
    }

    public void remove() {
        if (lastEntryReturned == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        HashMap.this.remove(lastEntryReturned.key); // <<< Hard coded call to HashMap's remove()
        lastEntryReturned = null;
        expectedModCount = modCount;
    }
}

private final class EntryIterator extends HashIterator
        implements Iterator<Entry<K, V>> {
    public Entry<K, V> next() { return nextEntry(); }
}

Extended HashMap classes do not have access to all private fields, so I cannot just modify the iterator, I would have to rewrite a lot of code to get my own iterator override that iterator.

Is there a way to completely override remove() so it gets called in every situation where HashMap.remove() was called?

ilomambo
  • 8,290
  • 12
  • 57
  • 106
  • Just a thought when seeing rows and columns - maybe guava's Table does what you need. – assylias Mar 18 '14 at 08:39
  • @assylias Yes, maybe, but that's a big change in my code I prefer to first try and resolve the bug issues. – ilomambo Mar 18 '14 at 08:41
  • 1
    To start with you should not extend `HashMap`; if you really need a custom implementation, extend `AbstractMap` instead. – fge Mar 18 '14 at 08:41
  • @fge Why I should not extend `HashMap`? If the class is not meant to be extended it would have been `final` – ilomambo Mar 18 '14 at 08:43
  • Just an example: `.removeAll()`. Nothing guarantees you that it will call `.remove()` if you just override `.remove()`! Another solution is to use delegation, or arrange for creating a class which does not require you to extend a map implementation in the first place – fge Mar 18 '14 at 08:46
  • @fge I want my `remove()` to be called only if the original `remove()` was called. If `removeAll()` calls `HashMap.remove()` I want,it to call my `remove()` instead. I think this is what overriding is for. – ilomambo Mar 18 '14 at 08:50
  • 1
    You didn't understand. What if `HashMap`'s `.removeAll()` _does not_ call `.remove()`? – fge Mar 18 '14 at 08:58
  • @fge If if HashMap's `.removeAll()` does not call `.remove()` it is fine, I don't want it call my new `.remove()` either. – ilomambo Mar 18 '14 at 09:00

0 Answers0