hashmap.java
来自「纯java操作系统jnode,安装简单和操作简单的个人使用的Java操作系统」· Java 代码 · 共 854 行 · 第 1/2 页
JAVA
854 行
for (int i = buckets.length - 1; i >= 0; i--) {
HashEntry e = buckets[i];
while (e != null) {
if (equals(value, e.value))
return true;
e = e.next;
}
}
return false;
}
/**
* Returns a shallow clone of this HashMap. The Map itself is cloned,
* but its contents are not. This is O(n).
*
* @return the clone
*/
public Object clone() {
HashMap copy = null;
try {
copy = (HashMap) super.clone();
} catch (CloneNotSupportedException x) {
// This is impossible.
}
copy.buckets = new HashEntry[buckets.length];
copy.putAllInternal(this);
// Clear the entry cache. AbstractMap.clone() does the others.
copy.entries = null;
return copy;
}
/**
* Returns a "set view" of this HashMap's keys. The set is backed by the
* HashMap, so changes in one show up in the other. The set supports
* element removal, but not element addition.
*
* @return a set view of the keys
* @see #values()
* @see #entrySet()
*/
public Set keySet() {
if (keys == null)
// Create an AbstractSet with custom implementations of those methods
// that can be overridden easily and efficiently.
keys = new AbstractSet() {
public int size() {
return size;
}
public Iterator iterator() {
// Cannot create the iterator directly, because of LinkedHashMap.
return HashMap.this.iterator(KEYS);
}
public void clear() {
HashMap.this.clear();
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
// Test against the size of the HashMap to determine if anything
// really got removed. This is necessary because the return value
// of HashMap.remove() is ambiguous in the null case.
int oldsize = size;
HashMap.this.remove(o);
return oldsize != size;
}
};
return keys;
}
/**
* Returns a "collection view" (or "bag view") of this HashMap's values.
* The collection is backed by the HashMap, so changes in one show up
* in the other. The collection supports element removal, but not element
* addition.
*
* @return a bag view of the values
* @see #keySet()
* @see #entrySet()
*/
public Collection values() {
if (values == null)
// We don't bother overriding many of the optional methods, as doing so
// wouldn't provide any significant performance advantage.
values = new AbstractCollection() {
public int size() {
return size;
}
public Iterator iterator() {
// Cannot create the iterator directly, because of LinkedHashMap.
return HashMap.this.iterator(VALUES);
}
public void clear() {
HashMap.this.clear();
}
};
return values;
}
/**
* Returns a "set view" of this HashMap's entries. The set is backed by
* the HashMap, so changes in one show up in the other. The set supports
* element removal, but not element addition.<p>
*
* Note that the iterators for all three views, from keySet(), entrySet(),
* and values(), traverse the HashMap in the same sequence.
*
* @return a set view of the entries
* @see #keySet()
* @see #values()
* @see Map.Entry
*/
public Set entrySet() {
if (entries == null)
// Create an AbstractSet with custom implementations of those methods
// that can be overridden easily and efficiently.
entries = new AbstractSet() {
public int size() {
return size;
}
public Iterator iterator() {
// Cannot create the iterator directly, because of LinkedHashMap.
return HashMap.this.iterator(ENTRIES);
}
public void clear() {
HashMap.this.clear();
}
public boolean contains(Object o) {
return getEntry(o) != null;
}
public boolean remove(Object o) {
HashEntry e = getEntry(o);
if (e != null) {
HashMap.this.remove(e.key);
return true;
}
return false;
}
};
return entries;
}
/**
* Helper method for put, that creates and adds a new Entry. This is
* overridden in LinkedHashMap for bookkeeping purposes.
*
* @param key the key of the new Entry
* @param value the value
* @param idx the index in buckets where the new Entry belongs
* @param callRemove whether to call the removeEldestEntry method
* @see #put(Object, Object)
*/
void addEntry(Object key, Object value, int idx, boolean callRemove) {
HashEntry e = new HashEntry(key, value);
e.next = buckets[idx];
buckets[idx] = e;
}
/**
* Helper method for entrySet(), which matches both key and value
* simultaneously.
*
* @param o the entry to match
* @return the matching entry, if found, or null
* @see #entrySet()
*/
// Package visible, for use in nested classes.
final HashEntry getEntry(Object o) {
if (!(o instanceof Map.Entry))
return null;
Map.Entry me = (Map.Entry) o;
Object key = me.getKey();
int idx = hash(key);
HashEntry e = buckets[idx];
while (e != null) {
if (equals(e.key, key))
return equals(e.value, me.getValue()) ? e : null;
e = e.next;
}
return null;
}
/**
* Helper method that returns an index in the buckets array for `key'
* based on its hashCode(). Package visible for use by subclasses.
*
* @param key the key
* @return the bucket number
*/
final int hash(Object key) {
try {
if (key == null) {
return 0;
} else {
return Math.abs(key.hashCode() % buckets.length);
}
} catch (Exception ex) {
ex.printStackTrace(System.err);
if (key != null) {
System.err.println("buckets.length = " + buckets.length + " key.hc=" + key.hashCode());
} else {
System.err.println("buckets.length = " + buckets.length + " key=" + key);
}
return 0;
}
}
/**
* Generates a parameterized iterator. Must be overrideable, since
* LinkedHashMap iterates in a different order.
*
* @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
* @return the appropriate iterator
*/
Iterator iterator(int type) {
return new HashIterator(type);
}
/**
* A simplified, more efficient internal implementation of putAll(). The
* Map constructor and clone() should not call putAll or put, in order to
* be compatible with the JDK implementation with respect to subclasses.
*
* @param m the map to initialize this from
*/
void putAllInternal(Map m) {
Iterator itr = m.entrySet().iterator();
int msize = m.size();
size = msize;
while (msize-- > 0) {
Map.Entry e = (Map.Entry) itr.next();
Object key = e.getKey();
int idx = hash(key);
addEntry(key, e.getValue(), idx, false);
}
}
/**
* Increases the size of the HashMap and rehashes all keys to new
* array indices; this is called when the addition of a new value
* would cause size() > threshold. Note that the existing Entry
* objects are reused in the new hash table.
*
* <p>This is not specified, but the new size is twice the current size
* plus one; this number is not always prime, unfortunately.
*/
private void rehash() {
HashEntry[] oldBuckets = buckets;
int newcapacity = (buckets.length * 2) + 1;
threshold = (int) (newcapacity * loadFactor);
buckets = new HashEntry[newcapacity];
for (int i = oldBuckets.length - 1; i >= 0; i--) {
HashEntry e = oldBuckets[i];
while (e != null) {
int idx = hash(e.key);
HashEntry dest = buckets[idx];
if (dest != null) {
while (dest.next != null)
dest = dest.next;
dest.next = e;
} else
buckets[idx] = e;
HashEntry next = e.next;
e.next = null;
e = next;
}
}
}
/**
* Serializes this object to the given stream.
*
* @param s the stream to write to
* @throws IOException if the underlying stream fails
* @serialData the <i>capacity</i>(int) that is the length of the
* bucket array, the <i>size</i>(int) of the hash map
* are emitted first. They are followed by size entries,
* each consisting of a key (Object) and a value (Object).
*/
private void writeObject(ObjectOutputStream s) throws IOException {
// Write the threshold and loadFactor fields.
s.defaultWriteObject();
s.writeInt(buckets.length);
s.writeInt(size);
// Avoid creating a wasted Set by creating the iterator directly.
Iterator it = iterator(ENTRIES);
while (it.hasNext()) {
HashEntry entry = (HashEntry) it.next();
s.writeObject(entry.key);
s.writeObject(entry.value);
}
}
/**
* Deserializes this object from the given stream.
*
* @param s the stream to read from
* @throws ClassNotFoundException if the underlying stream fails
* @throws IOException if the underlying stream fails
* @serialData the <i>capacity</i>(int) that is the length of the
* bucket array, the <i>size</i>(int) of the hash map
* are emitted first. They are followed by size entries,
* each consisting of a key (Object) and a value (Object).
*/
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
// Read the threshold and loadFactor fields.
s.defaultReadObject();
// Read and use capacity, followed by key/value pairs.
buckets = new HashEntry[s.readInt()];
int len = s.readInt();
size = len;
while (len-- > 0) {
Object key = s.readObject();
addEntry(key, s.readObject(), hash(key), false);
}
}
/**
* Iterate over HashMap's entries.
* This implementation is parameterized to give a sequential view of
* keys, values, or entries.
*
* @author Jon Zeppieri
*/
private final class HashIterator implements Iterator {
/**
* The type of this Iterator: {@link #KEYS}, {@link #VALUES},
* or {@link #ENTRIES}.
*/
private final int type;
/**
* The number of modifications to the backing HashMap that we know about.
*/
private int knownMod = modCount;
/** The number of elements remaining to be returned by next(). */
private int count = size;
/** Current index in the physical hash table. */
private int idx = buckets.length;
/** The last Entry returned by a next() call. */
private HashEntry last;
/**
* The next entry that should be returned by next(). It is set to something
* if we're iterating through a bucket that contains multiple linked
* entries. It is null if next() needs to find a new bucket.
*/
private HashEntry next;
/**
* Construct a new HashIterator with the supplied type.
* @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
*/
HashIterator(int type) {
this.type = type;
}
/**
* Returns true if the Iterator has more elements.
* @return true if there are more elements
* @throws ConcurrentModificationException if the HashMap was modified
*/
public boolean hasNext() {
if (knownMod != modCount)
throw new ConcurrentModificationException();
return count > 0;
}
/**
* Returns the next element in the Iterator's sequential view.
* @return the next element
* @throws ConcurrentModificationException if the HashMap was modified
* @throws NoSuchElementException if there is none
*/
public Object next() {
if (knownMod != modCount)
throw new ConcurrentModificationException();
if (count == 0)
throw new NoSuchElementException();
count--;
HashEntry e = next;
while (e == null)
e = buckets[--idx];
next = e.next;
last = e;
if (type == VALUES)
return e.value;
if (type == KEYS)
return e.key;
return e;
}
/**
* Removes from the backing HashMap the last element which was fetched
* with the <code>next()</code> method.
* @throws ConcurrentModificationException if the HashMap was modified
* @throws IllegalStateException if called when there is no last element
*/
public void remove() {
if (knownMod != modCount)
throw new ConcurrentModificationException();
if (last == null)
throw new IllegalStateException();
HashMap.this.remove(last.key);
last = null;
knownMod++;
}
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?