/*
* JCarder -- cards Java programs to keep threads disentangled
*
* Copyright (C) 2006-2007 Enea AB
* Copyright (C) 2007 Ulrik Svensson
* Copyright (C) 2007 Joel Rosdahl
*
* This program is made available under the GNU GPL version 2, with a special
* exception for linking with JUnit. See the accompanying file LICENSE.txt for
* details.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
package com.enea.jcarder.util;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import net.jcip.annotations.NotThreadSafe;
/**
* This class is similar to the java.util.WeakHashMap but compares objects with
* the == operator instead of with the Object.equals method.
*
* TODO Add basic tests for this class.
*/
@NotThreadSafe
public final class IdentityWeakHashMap<V> {
private final HashMap<IdentityComparableKey, V> mHashMap;
private final ReferenceQueue<Object> mReferenceQueue;
private final StrongKey mStrongKey = new StrongKey();
private Object mLastKey = null; // Cache to improve performance.
private V mLastValue = null; // Cache to improve performance.
private int mPutCounter = 0;
public IdentityWeakHashMap() {
mHashMap = new HashMap<IdentityComparableKey, V>();
mReferenceQueue = new ReferenceQueue<Object>();
}
public V get(Object key) {
/*
* Avoid calls to removeGarbageCollectedKeys in this method in order to
* improve performance.
*/
if (key == mLastKey) {
return mLastValue;
}
mLastKey = key;
mStrongKey.setReferent(key);
mLastValue = mHashMap.get(mStrongKey);
return mLastValue;
}
public void put(Object key, V value) {
assert value != null;
mLastKey = key;
mLastValue = value;
if (mPutCounter > 1000) {
// Don't call to often in order to improve performance.
removeGarbageCollectedKeys();
mPutCounter = 0;
} else {
mPutCounter++;
}
mHashMap.put((new WeakKey(key, mReferenceQueue)), value);
}
private void removeGarbageCollectedKeys() {
Reference e;
int noOfCollectedLocks = 0;
while ((e = mReferenceQueue.poll()) != null) {
noOfCollectedLocks++;
mHashMap.remove(e);
}
}
private static interface IdentityComparableKey {
Object get();
boolean equals(Object obj);
int hashCode();
}
private static class StrongKey implements IdentityComparableKey {
private Object mReferent;
void setReferent(Object referent) {
assert referent != null;
mReferent = referent;
}
public Object get() {
return mReferent;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
try {
IdentityComparableKey reference = (IdentityComparableKey) obj;
return (reference.get() == get());
} catch (ClassCastException e) {
return false;
}
}
public int hashCode() {
return System.identityHashCode(mReferent);
}
}
private static class WeakKey extends WeakReference<Object>
implements IdentityComparableKey {
private final int mHash;
WeakKey(Object referent, ReferenceQueue<Object> queue) {
super(referent, queue);
assert referent != null;
mHash = System.identityHashCode(referent);
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
try {
IdentityComparableKey reference = (IdentityComparableKey) obj;
Object referent = reference.get();
return (referent != null) && (referent == get());
} catch (ClassCastException e) {
return false;
}
}
public int hashCode() {
return mHash;
}
}
}