java集合系列——Map之HashMap介绍(八)

HashMap是基于哈希表的Map实现的的,一个Key对应一个Value,允许使用null键和null值,不保证映射的顺序,特别是它不保证该顺序恒久不变!也不是同步的。

大家好,又见面了,我是全栈君。

1.HashMap的简介 (JDK1.7.0_79版本)
HashMap是基于哈希表的Map实现的的,一个Key对应一个Value,允许使用null键和null值,不保证映射的顺序,特别是它不保证该顺序恒久不变!也不是同步的。
怎么理解不保证恒久不变呢?

当哈希表中的条目数超出了加载因子与当前容量的乘积时的时候,哈希表进行rehash操作(即重建内部数据结构),此时映射顺序可能会被打乱!

HashMap存放元素是通过哈希算法将其中的元素散列的存放在各个“桶”之间。

注:HashMap除了非同步和可以使用null之外,其他的和HashTable大致相同。so ,后面就不在介绍HashTable了!

2.HashMap的继承关系

类 HashMap<K,V>
java.lang.Object
  继承者 java.util.AbstractMap<K,V>
      继承者 java.util.HashMap<K,V>
类型参数:
    K - 此映射所维护的键的类型
    V - 所映射值的类型
所有已实现的接口: 
    Serializable, Cloneable, Map<K,V> 
  • 继承AbstractMap 抽象类, AbstractMap实现了Map接口中的一些方法,减少了重复代码!

  • 实现了Map、Cloneable、java.io.Serializable接口!

类图关系参看上一篇的Map介绍

3.HashMap的API

HashMap API
(1)构造函数:

HashMap() 
          构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。 
HashMap(int initialCapacity) 
          构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap。 
HashMap(int initialCapacity, float loadFactor) 
          构造一个带指定初始容量和加载因子的空 HashMap。 
HashMap(Map<? extends K,? extends V> m) 
          构造一个映射关系与指定 Map 相同的新 HashMap。 

(2)方法

 void clear() 
          从此映射中移除所有映射关系。 
 Object clone() 
          返回此 HashMap 实例的浅表副本:并不复制键和值本身。 
 boolean containsKey(Object key) 
          如果此映射包含对于指定键的映射关系,则返回 trueboolean containsValue(Object value) 
          如果此映射将一个或多个键映射到指定值,则返回 trueSet<Map.Entry<K,V>> entrySet() 
          返回此映射所包含的映射关系的 Set 视图。 
 V get(Object key) 
          返回指定键所映射的值;如果对于该键来说,此映射不包含任何映射关系,则返回 null。 
 boolean isEmpty() 
          如果此映射不包含键-值映射关系,则返回 trueSet<K> keySet() 
          返回此映射中所包含的键的 Set 视图。 
 V put(K key, V value) 
          在此映射中关联指定值与指定键。 
 void putAll(Map<? extends K,? extends V> m) 
          将指定映射的所有映射关系复制到此映射中,这些映射关系将替换此映射目前针对指定映射中所有键的所有映射关系。 
 V remove(Object key) 
          从此映射中移除指定键的映射关系(如果存在)。 
 int size() 
          返回此映射中的键-值映射关系数。 
 Collection<V> values() 
          返回此映射所包含的值的 Collection 视图。 

从类 java.util.AbstractMap 继承的方法
equals, hashCode, toString !

4.源码


public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{
/** * 初始化容量 - 必须是2的幂。 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 左移 相当于 2^4 = 16
/** * 最大的容量,容量必须是 必须是2的幂 并且小于等于 2^30(1<<30). * 若容量大于该值,则由该值替换! */
static final int MAXIMUM_CAPACITY = 1 << 30; //2^30
/** * 在构造函数中使用的加载因子(默认加载因子为0.75) */
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/** * An empty table instance to share when the table is not inflated. * 当表的容量不增加时候共享空表的实例 */
static final Entry<?,?>[] EMPTY_TABLE = {};
/** * The table, resized as necessary. Length MUST Always be a power of two. * 表,根据需要调整大小。长度必须一致是2的幂。 */
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
/** * The number of key-value mappings contained in this map. * 此Map中键值映射的数量(大小) */
transient int size;
/** * The next size value at which to resize (capacity * load factor). * 调整大小的下一个大小值(容量*负载系数)。 * @serial */
// HashMap的阈值,用于判断是否需要调整HashMap的容量(threshold = 容量*加载因子) 
int threshold;
/** * The load factor for the hash table. * 加载因子实际大小 * @serial */
final float loadFactor;
/** * 记录HashMap结构修改的次数,用于对HashMap的集合视图执行故障转移。 * 可以判断是否要抛出 ConcurrentModificationException 异常! */
transient int modCount;
/** * map容量的默认阈值,高于该值时,对字符串键使用备用散列。 * 替代散列减少了由于对于字符串键的散列码计算较弱而导致的冲突的发生率。 * * 此值可以通过定义系统属性jdk.map.althashing.threshold来覆盖。 * 属性值1强制在所有时间使用备用散列,而-1值确保不使用备用散列。 */
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;
/** * holds values which can't be initialized until after VM is booted. * 保存直到VM引导后才能初始化的值。 * 设置是否使用替代哈希 */
private static class Holder { 

/** * Table capacity above which to switch to use alternative hashing. * 表容量高于此时切换到使用替代哈希。 */
static final int ALTERNATIVE_HASHING_THRESHOLD;
static {
String altThreshold = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"jdk.map.althashing.threshold"));
int threshold;
try {
threshold = (null != altThreshold)
? Integer.parseInt(altThreshold)
: ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;
// disable alternative hashing if -1
// 如果-1 禁用替代哈希
if (threshold == -1) {
threshold = Integer.MAX_VALUE;
}
if (threshold < 0) {
throw new IllegalArgumentException("value must be positive integer.");
}
} catch(IllegalArgumentException failed) {
throw new Error("Illegal value for 'jdk.map.althashing.threshold'", failed);
}
ALTERNATIVE_HASHING_THRESHOLD = threshold;
}
}
/** * A randomizing value associated with this instance that is applied to * hash code of keys to make hash collisions harder to find. If 0 then * alternative hashing is disabled. * 与此实例相关联的随机化值,应用于密钥的哈希码,以使哈希碰撞更难以找到。 * 如果为0,则禁用备用散列。 */
transient int hashSeed = 0;
/** * 构造空HashMap,具有指定的初始容量和负载因子。 */
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor; //默认加载因子为0.75
threshold = initialCapacity; //默认初始化容量为 2^4 = 16
init();
}
/** * 构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap。 */
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/** * 构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。 */
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
/** * 构造一个映射关系与指定 Map 相同的新 HashMap。 */
public HashMap(Map<? extends K, ? extends V> m) {
// 新的map的初始化大小 : (size / 0.75(加载因子) ) + 1
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
inflateTable(threshold);//增容的table
putAllForCreate(m);
}
private static int roundUpToPowerOf2(int number) {
// assert number >= 0 : "number must be non-negative";
return number >= MAXIMUM_CAPACITY
? MAXIMUM_CAPACITY
: (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}
/** * Inflates the table. */
private void inflateTable(int toSize) {
// Find a power of 2 >= toSize
// 找到 2的幂 >= toSize
int capacity = roundUpToPowerOf2(toSize);
//这里判断 capacity * loadFactor, MAXIMUM_CAPACITY + 1 取最小值
threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
table = new Entry[capacity];//初始化table的容量
initHashSeedAsNeeded(capacity);
}
// internal utilities
/** *子类初始化 */
void init() {
}
/** * Initialize the hashing mask value. We defer initialization until we * really need it. * 初始化散列掩码值。 我们推迟初始化,直到我们真的需要它。 */
final boolean initHashSeedAsNeeded(int capacity) {
boolean currentAltHashing = hashSeed != 0;
boolean useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);//这里使用到了Holder.ALTERNATIVE_HASHING_THRESHOLD的值
boolean switching = currentAltHashing ^ useAltHashing;
if (switching) {
hashSeed = useAltHashing
? sun.misc.Hashing.randomHashSeed(this)
: 0;
}
return switching;
}
/** * 求hash值的方法,重新计算hash值 */
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/** *返回h在数组中的索引值,这里用&代替取模,旨在提升效率 */
static int indexFor(int h, int length) {
// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
return h & (length-1);
}
/** * 获取大小 */
public int size() {
return size;
}
/** *判断是否为空 */
public boolean isEmpty() {
return size == 0;
}
/** * get根据key获取value * * 分为可以等于null和不等于null */
public V get(Object key) {
if (key == null) 
return getForNullKey();
Entry<K,V> entry = getEntry(key);//通过key在Entry 里面查找value
return null == entry ? null : entry.getValue();
}
/** * 如果key 为null,判断map的大小是否为 0 ,为0 的话,返回null * 否则去Entry里面查询key == null,对于的Value值 */
private V getForNullKey() {
if (size == 0) {
return null;
}
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}
/** * 判断Map中是否有对应的key * 如果可以存在返回true,否则返回false */
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
/** * 返回与HashMap中的指定键相关联的条目。 如果HashMap不包含键的映射,则返回null。 */
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
/** * 往map中添加“key-value” * */
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)//若key == null,将value添加到table[0]
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//判断之前的Entry中是否对应的key,如果有对应的key,那么将之前的key对应的value进行替换
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
// 如果不存在key对应value对,则直接添加到table[i]处! 
addEntry(hash, key, value, i);
return null;
}
/** * 将“key为null”键值对添加到table[0]位置 */
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
//判断之前是否有key等于null,有的话,table[0]进行替换
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
// 如果不存在key为null的键值对,则直接添加到table[0]处! 
addEntry(0, null, value, 0);
return null;
}
/** * 内部方法。它被构造函数等调用。创建HashMap对应的“添加方法”,和put不同 。 */
private void putForCreate(K key, V value) {
int hash = null == key ? 0 : hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
e.value = value;
return;
}
}
createEntry(hash, key, value, i);
}
private void putAllForCreate(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
putForCreate(e.getKey(), e.getValue());
}
/** * 重新调整HashMap的大小,newCapacity是调整后的容量 */
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
//如果容量达到最大值,不在扩容,直接返回
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable, initHashSeedAsNeeded(newCapacity));
table = newTable;
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
/** * 将当前表中的所有条目添加到newTable。 */
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null != e) {
Entry<K,V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
/** * 将m中的元素全部添加到HashMap中 */
public void putAll(Map<? extends K, ? extends V> m) {
int numKeysToBeAdded = m.size();
if (numKeysToBeAdded == 0)//m的容量为0 ,直接返回
return;
if (table == EMPTY_TABLE) { //从新设置阈值
inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));
}
/* * m的容量大于阈值,扩容 */
if (numKeysToBeAdded > threshold) {
int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
if (targetCapacity > MAXIMUM_CAPACITY)
targetCapacity = MAXIMUM_CAPACITY;
int newCapacity = table.length;
while (newCapacity < targetCapacity)
newCapacity <<= 1;
if (newCapacity > table.length)
resize(newCapacity);//将此映射添加到更大的新的阵列
}
//容量足够。通过迭代器直接添加
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
/** * 删除“键为key”元素 */
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
/** * 删除“键为key”元素 */
final Entry<K,V> removeEntryForKey(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);//key==null ,hash==0
int i = indexFor(hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
while (e != null) {
Entry<K,V> next = e.next;
Object k;
//本质是“删除单向链表中的节点”
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
/** * 不做分析 */
final Entry<K,V> removeMapping(Object o) {
if (size == 0 || !(o instanceof Map.Entry))
}
/** * 清空HashMap */
public void clear() {
modCount++;
Arrays.fill(table, null);//使用了Arrays.fill方法
size = 0; //设置HashMap的大小为 0
}
/** * 判断HashMap中是否有对于的value * 有对应的value返回 true * 也是分为null了和非null进行判断 */
public boolean containsValue(Object value) {
if (value == null)
return containsNullValue();
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
/** *包含null参数的containsValue的特殊情况代码 */
private boolean containsNullValue() {
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (e.value == null)
return true;
return false;
}
/** * 实现了Cloneable,实现clone 方法 */
public Object clone() {
HashMap<K,V> result = null;
try {
result = (HashMap<K,V>)super.clone();
} catch (CloneNotSupportedException e) {
// assert false;
}
if (result.table != EMPTY_TABLE) {
result.inflateTable(Math.min(
(int) Math.min(
size * Math.min(1 / loadFactor, 4.0f),
// we have limits...
HashMap.MAXIMUM_CAPACITY),
table.length));
}
result.entrySet = null;
result.modCount = 0;
result.size = 0;
result.init();
// 调用putAllForCreate()将全部元素添加到HashMap中
result.putAllForCreate(this);
return result;
}
//Entry是单向链表。实现了Map.Entry<K,V>,
//即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数
static class Entry<K,V> implements Map.Entry<K,V> { 

final K key;
V value;
Entry<K,V> next;//下一个节点
int hash;
/** * 创建Entry * 参数包括"哈希值(h)", "键(k)", "值(v)", "下一节点(n)" */
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
public final K getKey() {
return key;
}
public final V getValue() {
return value;
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
//两个Entry相等。要求key和value都相等
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
//实现hashcode
public final int hashCode() {
return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
}
public final String toString() {
return getKey() + "=" + getValue();
}
void recordAccess(HashMap<K,V> m) {
}
void recordRemoval(HashMap<K,V> m) {
}
}
/** */
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);//容量扩为原来容量的2倍。
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
/** * */
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
//HashIterator是HashMap迭代器的抽象出来的父类,实现了公共了函数。 
private abstract class HashIterator<E> implements Iterator<E> { 

Entry<K,V> next;        // next entry to return
int expectedModCount;   // For fast-fail
int index;              // current slot
Entry<K,V> current;     // current entry
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
public final boolean hasNext() {
return next != null;
}
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
}
private final class ValueIterator extends HashIterator<V> { 

public V next() {
return nextEntry().value;
}
}
private final class KeyIterator extends HashIterator<K> { 

public K next() {
return nextEntry().getKey();
}
}
private final class EntryIterator extends HashIterator<Map.Entry<K,V>> { 

public Map.Entry<K,V> next() {
return nextEntry();
}
}
// Subclass overrides these to alter behavior of views' iterator() method
Iterator<K> newKeyIterator()   {
return new KeyIterator();
}
Iterator<V> newValueIterator()   {
return new ValueIterator();
}
Iterator<Map.Entry<K,V>> newEntryIterator()   {
return new EntryIterator();
}
// Views
// 查看HashMap中的元素,返回的是set集合
private transient Set<Map.Entry<K,V>> entrySet = null;
/** * 返回key的集合 */
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
private final class KeySet extends AbstractSet<K> { 

public Iterator<K> iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
public void clear() {
HashMap.this.clear();
}
}
/** * 继承AbstractCollection */
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null ? vs : (values = new Values()));
}
private final class Values extends AbstractCollection<V> { 

public Iterator<V> iterator() {
return newValueIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
HashMap.this.clear();
}
}
/** *返回map的映射的集合 */
public Set<Map.Entry<K,V>> entrySet() {
return entrySet0();
}
private Set<Map.Entry<K,V>> entrySet0() {
Set<Map.Entry<K,V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
private final class EntrySet extends AbstractSet<Map.Entry<K,V>> { 

public Iterator<Map.Entry<K,V>> iterator() {
return newEntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<K,V> e = (Map.Entry<K,V>) o;
Entry<K,V> candidate = getEntry(e.getKey());
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
return removeMapping(o) != null;
}
public int size() {
return size;
}
public void clear() {
HashMap.this.clear();
}
}
/** * 序列化 */
private void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
// Write out the threshold, loadfactor, and any hidden stuff
s.defaultWriteObject();
// Write out number of buckets
if (table==EMPTY_TABLE) {
s.writeInt(roundUpToPowerOf2(threshold));
} else {
s.writeInt(table.length);
}
// Write out size (number of Mappings)
s.writeInt(size);
// Write out keys and values (alternating)
if (size > 0) {
for(Map.Entry<K,V> e : entrySet0()) {
s.writeObject(e.getKey());
s.writeObject(e.getValue());
}
}
}
private static final long serialVersionUID = 362498820763181265L;
/** * 反序列话 */
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
// Read in the threshold (ignored), loadfactor, and any hidden stuff
s.defaultReadObject();
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
}
// set other fields that need values
table = (Entry<K,V>[]) EMPTY_TABLE;
// Read in number of buckets
s.readInt(); // ignored.
// Read number of mappings
int mappings = s.readInt();
if (mappings < 0)
throw new InvalidObjectException("Illegal mappings count: " +
mappings);
// capacity chosen by number of mappings and desired load (if >= 0.25)
int capacity = (int) Math.min(
mappings * Math.min(1 / loadFactor, 4.0f),
// we have limits...
HashMap.MAXIMUM_CAPACITY);
// allocate the bucket array;
if (mappings > 0) {
inflateTable(capacity);
} else {
threshold = capacity;
}
init();  // Give subclass a chance to do its thing.
// Read the keys and values, and put the mappings in the HashMap
for (int i = 0; i < mappings; i++) {
K key = (K) s.readObject();
V value = (V) s.readObject();
putForCreate(key, value);
}
}
// 这些方法在序列化HashSets时使用
int   capacity()     { return table.length; }// 返回“HashMap总的容量” 
float loadFactor()   { return loadFactor;   }// 返回“HashMap的加载因子” 
}

5.总结

(1):HashMap是通过哈希表来存储一个key-value的键值对,每个key对应一个value,允许key和value为null! hash
(2):HashMap 的实例有两个参数影响其性能:初始容量 和加载因子。容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。HashMap的容量不足的时候,可以自动扩容resize(),但是最大容量为MAXIMUM_CAPACITY==2^30!

(3):put和get都是分为null和非null进行判断!

(4):resize非常耗时的操作,因此,我们在用HashMap的时,最好能提前预估下HashMap中元素的个数,这样有助于提高HashMap的性能。
(5):求hash值和索引值的方法,这两个方法便是HashMap设计的最为核心的部分,二者结合能保证哈希表中的元素尽可能均匀地散列。

引用:

HashMap中则通过h&(length-1)的方法来代替取模,同样实现了均匀的散列,但效率要高很多,这也是HashMap对Hashtable的一个改进。  
接下来,我们分析下为什么哈希表的容量一定要是2的整数次幂。    
首先,length2的整数次幂的话,h&(length-1)就相当于对length取模,这样便保证了散列的均匀,同时也提升了效率;  
其次,length2的整数次幂的话,为偶数,这样length-1为奇数,奇数的最后一位是1,这样便保证了h&(length-1)的最后一位可能为0,也可能为1(这取决于h的值),即与后的结果可能为偶数,也可能为奇数,  
这样便可以保证散列的均匀性,而如果length为奇数的话,很明显length-1为偶数,它的最后一位是0,这样h&(length-1)的最后一位肯定为0,即只能为偶数,这样任何hash值都只会被散列到数组的偶数下标位置上,这便浪费了近一半的空间,
因此,length2的整数次幂,是为了使不同hash值发生碰撞的概率较小,这样就能使元素在哈希表中均匀地散列。

注:用对象当做hashmap的key进行存储要注意什么?

  • 1.要注意这个对象是否为可变对象。 http://www.importnew.com/13384.html

  • 2.要重写hashcode 方法,因为在HashMap的源代码里面,是先比较HashCode是否相等,同时要满足引用相等或者equals相等。


参考文章:
HashMap源码剖析:http://blog.csdn.net/ns_code/article/details/36034955

附:

JDK8中HashMap的底层实现:http://ericchunli.iteye.com/blog/2356721


java集合系列——java集合概述(一)
java集合系列——List集合之ArrayList介绍(二)
java集合系列——List集合之LinkedList介绍(三)
java集合系列——List集合之Vector介绍(四)
java集合系列——List集合之Stack介绍(五)
java集合系列——List集合总结(六)
java集合系列——Map介绍(七)
java集合系列——Map之HashMap介绍(八)
java集合系列——Map之TreeMap介绍(九)
java集合系列——Set之HashSet和TreeSet介绍(十)



如果帅气(美丽)、睿智(聪颖),和我一样简单善良的你看到本篇博文中存在问题,请指出,我虚心接受你让我成长的批评,谢谢阅读!
祝你今天开心愉快!


欢迎访问我的csdn博客,我们一同成长!

不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

博客首页http://blog.csdn.net/u010648555

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/121181.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Java数组(Array)

    Java数组(Array)数组(Array),是多个相同类型数据一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。数组相关的概念:数组名元素角标、下标、索引数组的长度:元素的个数数组的特点:数组是按序排列的数组属于引用数据类型的变量。数组的元素,既可以是基本数据类型,也可以是引用数据类型创建数组对象会在内存中开辟一整块连续的空间数组的长度一旦确定,就不能修改。数组…

  • dropdownlist绑定数据源_不能绑定到字段或数据成员

    dropdownlist绑定数据源_不能绑定到字段或数据成员如何使用DropDownList控件绑定数据呢,今天我们来介绍一下比较常用的一种方法——前后台结合方式:首先,我们需要拉一个DropDownList控件:然后,通过控件配置SqlDataSource数据源,选择合适的数据表:接着,设置DataTextField(数据源中提供项文本的字段)和DataValueField(数据源中提供项值的字段)属性:前台显示如下:配置完之后,一定不要忘记删除DataSourceID属性和生成的SqlDataSource控件:

  • linux vlc乱码,一劳永逸解决VLC播放中文字幕乱码问题

    linux vlc乱码,一劳永逸解决VLC播放中文字幕乱码问题VLC对于Mac/Ubuntu用户来说算得上是必备软件。其相当于PC机上的“暴风影音”,但Mac/Ubuntu的新手使用VLC播放avi时都会碰到字幕乱码的问题。avi字幕的格式有多种,这里假设你使用常见的.srt字幕。VLC默认支持的字幕内码为utf-8,而网上提供的.srt字幕基本上都是GBK码,所以在初装VLC后的默认状态下,加载.srt字幕都会出现乱码。本教程以当前最新的VLC1.1…

  • MAC下Wireshark权限问题(you don’t have permission to capture on that device Mac)[通俗易懂]

    MAC下Wireshark权限问题(you don’t have permission to capture on that device Mac)

  • 迁移学习简介及用途[通俗易懂]

    迁移学习简介及用途 https://mp.weixin.qq.com/s/5_EYEJUycTtpfbxM_uGwHw  ———————本文来自mishidemudong的CSDN博客  深度神经网络,相比于之前的传统机器学习方法,可以看成是一个全新的物种,这背后的原因,最明显的还是深度学习对机器算力的巨大需求,在深度学习入门最少需要知…

  • 编译链接过程中出现“无法解析的外部符号”,原因及解决办法总结

    编译链接过程中出现“无法解析的外部符号”,原因及解决办法总结1、错误为:errorLNK2019:无法解析的外部符号___report_rangecheckfailure,该符号在函数_OBJ_create_objects中被引用 原因:__report_rangecheckfailure  是用来检查堆栈缓存溢出的,如果编译的时候打开GS(projectproperty–》Configurationproperties–》c/c++…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号