IdentityMap.java
package org.codefilarete.tool.collection;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Storage of key-value pairs based on key {@link System#identityHashCode} to avoid loss of bean in classical
* {@link Map} : because those are based on instance hashCode, beans can't be retrieved due to some hashCode change
* when computation is based on incompletely filled attributes, such as collection.
* <p>
* It does not implement {@link Map} because it is mainly used as a marking name instead of the anonymous Map class
* which only instantiation brings the implementation : by this name the developer clearly says its intention (and
* should add a comment why such a Map is required in its algorithm ;) ).
* It could also have been replaced by {@link java.util.IdentityHashMap} (or use it internally) but, first it would
* have broken initial need, and overall one have to know that {@link java.util.IdentityHashMap} compares keys on their
* {@link System#identityHashCode} but also its values which, beyond being not really intuitive, might not be required
* by production code, and brings some difficulties in tests (because even same Strings are different with '==').
*
* @param <K> key type
* @param <V> value type
*/
public class IdentityMap<K, V> {
private final Map<Integer, V> delegate;
public IdentityMap() {
this.delegate = new HashMap<>();
}
public IdentityMap(int capacity) {
this.delegate = new HashMap<>(capacity);
}
protected int hash(K key) {
return System.identityHashCode(key);
}
public V put(K key, V value) {
return this.delegate.put(hash(key), value);
}
public V get(K key) {
return this.delegate.get(hash(key));
}
public boolean containsKey(K key) {
return delegate.containsKey(hash(key));
}
public V putIfAbsent(K key, V value) {
return delegate.putIfAbsent(hash(key), value);
}
public V putIfAbsent(K key, Supplier<V> value) {
return delegate.putIfAbsent(hash(key), value.get());
}
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
return delegate.computeIfAbsent(hash(key), k -> mappingFunction.apply(key));
}
public Collection<V> values() {
return delegate.values();
}
public V remove(K key) {
return this.delegate.remove(hash(key));
}
public void clear() {
this.delegate.clear();
}
public int size() {
return this.delegate.size();
}
/**
* Exposes internal storage, made for testing purpose, not expected to be used in production
* @return internal storage
*/
public Map<Integer, V> getDelegate() {
return delegate;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IdentityMap<?, ?> that = (IdentityMap<?, ?>) o;
return delegate.equals(that.delegate);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
/**
* Implemented for easier debug
*
* @return delegate's toString()
*/
@Override
public String toString() {
return delegate.toString();
}
}