📄 abstracthashedmap.java
字号:
return oldValue;
}
previous = entry;
entry = entry.next;
}
return null;
}
/**
* Clears the map, resetting the size to zero and nullifying references to
* avoid garbage collection issues.
*/
public void clear() {
this.modCount++;
final HashEntry[] data = this.data;
for ( int i = data.length - 1; i >= 0; i-- ) {
data[i] = null;
}
this.size = 0;
}
// -----------------------------------------------------------------------
/**
* Converts input keys to another object for storage in the map. This
* implementation masks nulls. Subclasses can override this to perform
* alternate key conversions.
* <p>
* The reverse conversion can be changed, if required, by overriding the
* getKey() method in the hash entry.
*
* @param key
* the key convert
* @return the converted key
*/
protected Object convertKey(final Object key) {
return (key == null ? AbstractHashedMap.NULL : key);
}
/**
* Gets the hash code for the key specified. This implementation uses the
* additional hashing routine from JDK1.4. Subclasses can override this to
* return alternate hash codes.
*
* @param key
* the key to get a hash code for
* @return the hash code
*/
protected int hash(final Object key) {
// same as JDK 1.4
int h = key.hashCode();
h += ~(h << 9);
h ^= (h >>> 14);
h += (h << 4);
h ^= (h >>> 10);
return h;
}
/**
* Compares two keys, in internal converted form, to see if they are equal.
* This implementation uses the equals method and assumes neither key is
* null. Subclasses can override this to match differently.
*
* @param key1
* the first key to compare passed in from outside
* @param key2
* the second key extracted from the entry via
* <code>entry.key</code>
* @return true if equal
*/
protected boolean isEqualKey(final Object key1,
final Object key2) {
return (key1 == key2 || key1.equals( key2 ));
}
/**
* Compares two values, in external form, to see if they are equal. This
* implementation uses the equals method and assumes neither value is null.
* Subclasses can override this to match differently.
*
* @param value1
* the first value to compare passed in from outside
* @param value2
* the second value extracted from the entry via
* <code>getValue()</code>
* @return true if equal
*/
protected boolean isEqualValue(final Object value1,
final Object value2) {
return (value1 == value2 || value1.equals( value2 ));
}
/**
* Gets the index into the data storage for the hashCode specified. This
* implementation uses the least significant bits of the hashCode.
* Subclasses can override this to return alternate bucketing.
*
* @param hashCode
* the hash code to use
* @param dataSize
* the size of the data to pick a bucket from
* @return the bucket index
*/
protected int hashIndex(final int hashCode,
final int dataSize) {
return hashCode & (dataSize - 1);
}
// -----------------------------------------------------------------------
/**
* Gets the entry mapped to the key specified.
* <p>
* This method exists for subclasses that may need to perform a multi-step
* process accessing the entry. The public methods in this class don't use
* this method to gain a small performance boost.
*
* @param key
* the key
* @return the entry, null if no match
*/
protected HashEntry getEntry(Object key) {
key = convertKey( key );
final int hashCode = hash( key );
HashEntry entry = this.data[hashIndex( hashCode,
this.data.length )]; // no local
// for hash
// index
while ( entry != null ) {
if ( entry.hashCode == hashCode && isEqualKey( key,
entry.key ) ) {
return entry;
}
entry = entry.next;
}
return null;
}
// -----------------------------------------------------------------------
/**
* Updates an existing key-value mapping to change the value.
* <p>
* This implementation calls <code>setValue()</code> on the entry.
* Subclasses could override to handle changes to the map.
*
* @param entry
* the entry to update
* @param newValue
* the new value to store
*/
protected void updateEntry(final HashEntry entry,
final Object newValue) {
entry.setValue( newValue );
}
/**
* Reuses an existing key-value mapping, storing completely new data.
* <p>
* This implementation sets all the data fields on the entry. Subclasses
* could populate additional entry fields.
*
* @param entry
* the entry to update, not null
* @param hashIndex
* the index in the data array
* @param hashCode
* the hash code of the key to add
* @param key
* the key to add
* @param value
* the value to add
*/
protected void reuseEntry(final HashEntry entry,
final int hashIndex,
final int hashCode,
final Object key,
final Object value) {
entry.next = this.data[hashIndex];
entry.hashCode = hashCode;
entry.key = key;
entry.value = value;
}
// -----------------------------------------------------------------------
/**
* Adds a new key-value mapping into this map.
* <p>
* This implementation calls <code>createEntry()</code>,
* <code>addEntry()</code> and <code>checkCapacity()</code>. It also
* handles changes to <code>modCount</code> and <code>size</code>.
* Subclasses could override to fully control adds to the map.
*
* @param hashIndex
* the index into the data array to store at
* @param hashCode
* the hash code of the key to add
* @param key
* the key to add
* @param value
* the value to add
*/
protected void addMapping(final int hashIndex,
final int hashCode,
final Object key,
final Object value) {
this.modCount++;
final HashEntry entry = createEntry( this.data[hashIndex],
hashCode,
key,
value );
addEntry( entry,
hashIndex );
this.size++;
checkCapacity();
}
/**
* Creates an entry to store the key-value data.
* <p>
* This implementation creates a new HashEntry instance. Subclasses can
* override this to return a different storage class, or implement caching.
*
* @param next
* the next entry in sequence
* @param hashCode
* the hash code to use
* @param key
* the key to store
* @param value
* the value to store
* @return the newly created entry
*/
protected HashEntry createEntry(final HashEntry next,
final int hashCode,
final Object key,
final Object value) {
return new HashEntry( next,
hashCode,
key,
value );
}
/**
* Adds an entry into this map.
* <p>
* This implementation adds the entry to the data storage table. Subclasses
* could override to handle changes to the map.
*
* @param entry
* the entry to add
* @param hashIndex
* the index into the data array to store at
*/
protected void addEntry(final HashEntry entry,
final int hashIndex) {
this.data[hashIndex] = entry;
}
// -----------------------------------------------------------------------
/**
* Removes a mapping from the map.
* <p>
* This implementation calls <code>removeEntry()</code> and
* <code>destroyEntry()</code>. It also handles changes to
* <code>modCount</code> and <code>size</code>. Subclasses could
* override to fully control removals from the map.
*
* @param entry
* the entry to remove
* @param hashIndex
* the index into the data structure
* @param previous
* the previous entry in the chain
*/
protected void removeMapping(final HashEntry entry,
final int hashIndex,
final HashEntry previous) {
this.modCount++;
removeEntry( entry,
hashIndex,
previous );
this.size--;
destroyEntry( entry );
}
/**
* Removes an entry from the chain stored in a particular index.
* <p>
* This implementation removes the entry from the data storage table. The
* size is not updated. Subclasses could override to handle changes to the
* map.
*
* @param entry
* the entry to remove
* @param hashIndex
* the index into the data structure
* @param previous
* the previous entry in the chain
*/
protected void removeEntry(final HashEntry entry,
final int hashIndex,
final HashEntry previous) {
if ( previous == null ) {
this.data[hashIndex] = entry.next;
} else {
previous.next = entry.next;
}
}
/**
* Kills an entry ready for the garbage collector.
* <p>
* This implementation prepares the HashEntry for garbage collection.
* Subclasses can override this to implement caching (override clear as
* well).
*
* @param entry
* the entry to destroy
*/
protected void destroyEntry(final HashEntry entry) {
entry.next = null;
entry.key = null;
entry.value = null;
}
// -----------------------------------------------------------------------
/**
* Checks the capacity of the map and enlarges it if necessary.
* <p>
* This implementation uses the threshold to check if the map needs
* enlarging
*/
protected void checkCapacity() {
if ( this.size >= this.threshold ) {
final int newCapacity = this.data.length * 2;
if ( newCapacity <= AbstractHashedMap.MAXIMUM_CAPACITY ) {
ensureCapacity( newCapacity );
}
}
}
/**
* Changes the size of the data structure to the capacity proposed.
*
* @param newCapacity
* the new capacity of the array (a power of two, less or equal
* to max)
*/
protected void ensureCapacity(final int newCapacity) {
final int oldCapacity = this.data.length;
if ( newCapacity <= oldCapacity ) {
return;
}
if ( this.size == 0 ) {
this.threshold = calculateThreshold( newCapacity,
this.loadFactor );
this.data = new HashEntry[newCapacity];
} else {
final HashEntry oldEntries[] = this.data;
final HashEntry newEntries[] = new HashEntry[newCapacity];
this.modCount++;
for ( int i = oldCapacity - 1; i >= 0; i-- ) {
HashEntry entry = oldEntries[i];
if ( entry != null ) {
oldEntries[i] = null; // gc
do {
final HashEntry next = entry.next;
final int index = hashIndex( entry.hashCode,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -