MethodMappublic final class MethodMap extends HashMap This is an optimized map for resolving java.lang.reflect.Method objects.
Doing a method lookup, even on an unsynchronized Map, can be an
expensive operation, in many cases taking multiple microseconds.
In most situations this overhead is negligible, but it can be noticeable
when performed in the common path of a local ejb invocation, where our
goal is to be as fast as a raw java method call.
A MethodMap must be created with an existing Map and is immutable after
construction(except for clear()).
It does not support the optional Map operations
put, putAll, and remove. NOTE that these operations could
be implemented but are not necessary at this point since the main use
is for the container's method info, which is invariant after initialization.
As this is a map for Method objects, null keys are not supported.
This map is unsynchronized. |
Fields Summary |
---|
private static final int | DEFAULT_BUCKET_MULTIPLIER | private int | numBuckets_ | private MethodInfo[] | methodInfo_ |
Constructors Summary |
---|
public MethodMap(Map methodMap)
super(methodMap);
numBuckets_ = methodMap.size() * DEFAULT_BUCKET_MULTIPLIER;
buildLookupTable(methodMap);
| public MethodMap(Map methodMap, int numBuckets)
super(methodMap);
if( numBuckets <= 0 ) {
throw new IllegalArgumentException
("Invalid value of numBuckets = " + numBuckets);
}
numBuckets_ = numBuckets;
buildLookupTable(methodMap);
|
Methods Summary |
---|
private void | buildLookupTable(java.util.Map methodMap)
methodInfo_ = new MethodInfo[numBuckets_];
Set methods = methodMap.keySet();
Set occupied = new HashSet();
for(Iterator iter = methods.iterator(); iter.hasNext();) {
Object nextObj = iter.next();
Method next = null;
if( nextObj == null ) {
throw new IllegalStateException("null keys not supported");
} else if( nextObj instanceof Method ) {
next = (Method) nextObj;
} else {
throw new IllegalStateException
("invalid key type = " + nextObj.getClass() +
" key must be of type java.lang.reflect.Method");
}
int bucket = getBucket(next);
if( !occupied.contains(new Integer(bucket)) ) {
MethodInfo methodInfo = new MethodInfo();
methodInfo.key = next;
methodInfo.value = methodMap.get(next);
// cache declaring class so we can avoid the method call
// during lookup operation.
methodInfo.declaringClass = next.getDeclaringClass();
methodInfo_[bucket] = methodInfo;
occupied.add(new Integer(bucket));
} else {
// there's a clash for this bucket, so null it out and
// defer to backing HashMap for results.
methodInfo_[bucket] = null;
}
}
| public void | clear()
if( methodInfo_ != null ) {
methodInfo_ = null;
super.clear();
}
| public java.lang.Object | get(java.lang.Object key)
if( key instanceof Method ) {
Method m = (Method) key;
Class[] paramTypes = m.getParameterTypes();
return get(m, paramTypes.length);
}
return null;
| public java.lang.Object | get(java.lang.reflect.Method m, int numParams)
if( methodInfo_ == null ) {
return null;
} else if( numParams < 0 ) {
throw new IllegalStateException
("invalid numParams = " + numParams);
}
Object value = null;
MethodInfo methodInfo = methodInfo_[getBucket(m, numParams)];
if( methodInfo != null) {
// Declaring classes must be the same for methods to be equal.
if(methodInfo.declaringClass == m.getDeclaringClass()) {
value = methodInfo.value;
}
}
return (value != null) ? value : super.get(m);
| private final int | getBucket(java.lang.reflect.Method m)
// note : getParameterTypes is guaranteed to be 0-length array
// (as opposed to null) for a method with no arguments.
Class[] paramTypes = m.getParameterTypes();
return getBucket(m, paramTypes.length);
| private final int | getBucket(java.lang.reflect.Method m, int numParams)
String methodName = m.getName();
// The normal Method.hashCode() method makes 5 method calls
// and does not cache the result. Here, we use the method name's
// hashCode since String.hashCode() makes 0 method calls *and* caches
// the result. The tradeoff is that using only method name will
// not account for overloaded methods, so we also add the number of
// parameters to the calculation. In many cases, the caller
// already knows the number of parameters, so it can be passed in
// to the lookup. This gives up some encapsulation for
// speed. It will result in better performance because
// we can skip the call to m.getClass().getParameterTypes(),
// which results in multiple method calls and can involve some
// expensive copying depending of the types themselves.
// Of course, this still won't account for the case where methods
// are overloaded with the same number of parameters but different
// types. However, the cache miss penalty should be small enough
// in this case that it's a fair tradeoff. Adding anything else
// to the hashcode calculation will have too large an impact on the
// common case.
int hashCode = methodName.hashCode();
// account for negative hashcodes
hashCode = (hashCode >= 0) ? hashCode : (hashCode * -1);
hashCode = (hashCode > numParams) ?
(hashCode - numParams) : (hashCode + numParams);
return (hashCode % numBuckets_);
| public java.lang.Object | put(java.lang.Object key, java.lang.Object value)
throw new UnsupportedOperationException();
| public void | putAll(java.util.Map t)
throw new UnsupportedOperationException();
| public java.lang.Object | remove(java.lang.Object key)
throw new UnsupportedOperationException();
|
|