📄 abstractreferencemap.java
字号:
* The reference is registered with the queue for later purging.
*
* @param type HARD, SOFT or WEAK
* @param referent the object to refer to
* @param hash the hash code of the <i>key</i> of the mapping;
* this number might be different from referent.hashCode() if
* the referent represents a value and not a key
*/
protected Object toReference(int type, Object referent, int hash) {
switch (type) {
case HARD: return referent;
case SOFT: return new SoftRef(hash, referent, parent.queue);
case WEAK: return new WeakRef(hash, referent, parent.queue);
default: throw new Error();
}
}
/**
* Purges the specified reference
* @param ref the reference to purge
* @return true or false
*/
boolean purge(Reference ref) {
boolean r = (parent.keyType > HARD) && (key == ref);
r = r || ((parent.valueType > HARD) && (value == ref));
if (r) {
if (parent.keyType > HARD) {
((Reference)key).clear();
}
if (parent.valueType > HARD) {
((Reference)value).clear();
} else if (parent.purgeValues) {
value = null;
}
}
return r;
}
/**
* Gets the next entry in the bucket.
*
* @return the next entry in the bucket
*/
protected ReferenceEntry next() {
return (ReferenceEntry) next;
}
}
//-----------------------------------------------------------------------
/**
* The EntrySet iterator.
*/
static class ReferenceEntrySetIterator implements Iterator {
/** The parent map */
final AbstractReferenceMap parent;
// These fields keep track of where we are in the table.
int index;
ReferenceEntry entry;
ReferenceEntry previous;
// These Object fields provide hard references to the
// current and next entry; this assures that if hasNext()
// returns true, next() will actually return a valid element.
Object nextKey, nextValue;
Object currentKey, currentValue;
int expectedModCount;
public ReferenceEntrySetIterator(AbstractReferenceMap parent) {
super();
this.parent = parent;
index = (parent.size() != 0 ? parent.data.length : 0);
// have to do this here! size() invocation above
// may have altered the modCount.
expectedModCount = parent.modCount;
}
public boolean hasNext() {
checkMod();
while (nextNull()) {
ReferenceEntry e = entry;
int i = index;
while ((e == null) && (i > 0)) {
i--;
e = (ReferenceEntry) parent.data[i];
}
entry = e;
index = i;
if (e == null) {
currentKey = null;
currentValue = null;
return false;
}
nextKey = e.getKey();
nextValue = e.getValue();
if (nextNull()) {
entry = entry.next();
}
}
return true;
}
private void checkMod() {
if (parent.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
private boolean nextNull() {
return (nextKey == null) || (nextValue == null);
}
protected ReferenceEntry nextEntry() {
checkMod();
if (nextNull() && !hasNext()) {
throw new NoSuchElementException();
}
previous = entry;
entry = entry.next();
currentKey = nextKey;
currentValue = nextValue;
nextKey = null;
nextValue = null;
return previous;
}
protected ReferenceEntry currentEntry() {
checkMod();
return previous;
}
public Object next() {
return nextEntry();
}
public void remove() {
checkMod();
if (previous == null) {
throw new IllegalStateException();
}
parent.remove(currentKey);
previous = null;
currentKey = null;
currentValue = null;
expectedModCount = parent.modCount;
}
}
/**
* The keySet iterator.
*/
static class ReferenceKeySetIterator extends ReferenceEntrySetIterator {
ReferenceKeySetIterator(AbstractReferenceMap parent) {
super(parent);
}
public Object next() {
return nextEntry().getKey();
}
}
/**
* The values iterator.
*/
static class ReferenceValuesIterator extends ReferenceEntrySetIterator {
ReferenceValuesIterator(AbstractReferenceMap parent) {
super(parent);
}
public Object next() {
return nextEntry().getValue();
}
}
/**
* The MapIterator implementation.
*/
static class ReferenceMapIterator extends ReferenceEntrySetIterator implements MapIterator {
protected ReferenceMapIterator(AbstractReferenceMap parent) {
super(parent);
}
public Object next() {
return nextEntry().getKey();
}
public Object getKey() {
HashEntry current = currentEntry();
if (current == null) {
throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
}
return current.getKey();
}
public Object getValue() {
HashEntry current = currentEntry();
if (current == null) {
throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
}
return current.getValue();
}
public Object setValue(Object value) {
HashEntry current = currentEntry();
if (current == null) {
throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
}
return current.setValue(value);
}
}
//-----------------------------------------------------------------------
// These two classes store the hashCode of the key of
// of the mapping, so that after they're dequeued a quick
// lookup of the bucket in the table can occur.
/**
* A soft reference holder.
*/
static class SoftRef extends SoftReference {
/** the hashCode of the key (even if the reference points to a value) */
private int hash;
public SoftRef(int hash, Object r, ReferenceQueue q) {
super(r, q);
this.hash = hash;
}
public int hashCode() {
return hash;
}
}
/**
* A weak reference holder.
*/
static class WeakRef extends WeakReference {
/** the hashCode of the key (even if the reference points to a value) */
private int hash;
public WeakRef(int hash, Object r, ReferenceQueue q) {
super(r, q);
this.hash = hash;
}
public int hashCode() {
return hash;
}
}
//-----------------------------------------------------------------------
/**
* Replaces the superclass method to store the state of this class.
* <p>
* Serialization is not one of the JDK's nicest topics. Normal serialization will
* initialise the superclass before the subclass. Sometimes however, this isn't
* what you want, as in this case the <code>put()</code> method on read can be
* affected by subclass state.
* <p>
* The solution adopted here is to serialize the state data of this class in
* this protected method. This method must be called by the
* <code>writeObject()</code> of the first serializable subclass.
* <p>
* Subclasses may override if they have a specific field that must be present
* on read before this implementation will work. Generally, the read determines
* what must be serialized here, if anything.
*
* @param out the output stream
*/
protected void doWriteObject(ObjectOutputStream out) throws IOException {
out.writeInt(keyType);
out.writeInt(valueType);
out.writeBoolean(purgeValues);
out.writeFloat(loadFactor);
out.writeInt(data.length);
for (MapIterator it = mapIterator(); it.hasNext();) {
out.writeObject(it.next());
out.writeObject(it.getValue());
}
out.writeObject(null); // null terminate map
// do not call super.doWriteObject() as code there doesn't work for reference map
}
/**
* Replaces the superclassm method to read the state of this class.
* <p>
* Serialization is not one of the JDK's nicest topics. Normal serialization will
* initialise the superclass before the subclass. Sometimes however, this isn't
* what you want, as in this case the <code>put()</code> method on read can be
* affected by subclass state.
* <p>
* The solution adopted here is to deserialize the state data of this class in
* this protected method. This method must be called by the
* <code>readObject()</code> of the first serializable subclass.
* <p>
* Subclasses may override if the subclass has a specific field that must be present
* before <code>put()</code> or <code>calculateThreshold()</code> will work correctly.
*
* @param in the input stream
*/
protected void doReadObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
this.keyType = in.readInt();
this.valueType = in.readInt();
this.purgeValues = in.readBoolean();
this.loadFactor = in.readFloat();
int capacity = in.readInt();
init();
data = new HashEntry[capacity];
while (true) {
Object key = in.readObject();
if (key == null) {
break;
}
Object value = in.readObject();
put(key, value);
}
threshold = calculateThreshold(data.length, loadFactor);
// do not call super.doReadObject() as code there doesn't work for reference map
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -