首先看HashMap,他是最基础的
他的内部结构首先是一个装着结点的数组,然后每一个结点又有下一个结点,也就是一个链表,好像是叫 链表散列 结构
//数组
//transient关键字表示在对象序列化的时候忽略该属性
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
//结点的数据结构
final K key;
V value;
Entry<K,V> next;
int hash;
注意下面的equals方法,传进去的参数为Object类型,所以你如果自定义了equals方法,但是参数不是Object,那就是重载,不是覆盖,不会被调用
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
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;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
当往HashMap里面存储值的时候,会先判断容量是否足够,不够的时候会扩大容量
然后判断键是否为空,若为空则调用return putForNullKey(value);
或不为空则先把key的hash值再hash一次,此算法加入了高位计算,防止低位不变,高位变化时,造成的hash冲突。但代码里写的是hash(key)对吧,点进去看他的实现就可以看到是对他的hashcode再hash,我的是jdk1.7,好像在之前的是直接写的hash(key.hashcode);
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);
}
然后根据这个值计算他在数组中对应的下标。我们想到的就是对数组长度取模,但是不是这样做的
*/
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);
}
然后对该下标所对应的链表进行遍历,
若存在,改变value,返回,若不存在,把key和value放入,返回null。
放在最前面,在前面,最前面
之前在网上看到判断是否存在是先判断hashcode,若相等再调用equals方法,但是看实际的源码应该不是这样做的。
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
这里的hash也不是之前的hashcode,是再hash过的。
而且若hash值相等,也不是调用的equals方法,而是直接用的==运算符
equals作为单独的一个判断条件(若equals成立,那么hashcode必定相等)。
get方法与put方法类似,进行查找然后返回即可。
HashTable是线程安全的HashMap,但是速度比较慢,他和HashMap的一个区别是他不能存放NULL键,他使用的hash值就是hashCode方法得出的hash值,而HashMap的hash值是再次计算过的
int hash = key.hashCode();
int hash = hash(key);
CurrentHashMap
是为了满足人们的欲望,既要线程安全,又要速度快。
他把一个大的map分为几个小的,然后在每一个小的map里实现线程安全,加快了速度。
在put时首先计算他属于那一段,然后在如同hashmap一样操作
读操作:使用volatile关键字,保证获取到的是最新的值
写操作:它会先获取该Key-Value对所在的Segment的锁,获取成功后就可以像操作一个普通的HashMap一样操作该Segment,并保证该Segment的安全性。
获取锁时,并不直接使用lock来获取,因为该方法获取锁失败时会挂起(参考可重入锁)。事实上,它使用了自旋锁,如果tryLock获取锁失败,说明锁被其它线程占用,此时通过循环再次以tryLock的方式申请锁。如果在循环过程中该Key所对应的链表头被修改,则重置retry次数。如果retry次数超过一定值,则使用lock方法申请锁。
HashSet
它里面实际上就是采用的HashMap,把值当作map的键
值用的是一个Object
//HashSet里面的每一个值当作key,然后值就是这个Object
private static final Object PRESENT = new Object();
//HashSet中的HashMap
private transient HashMap<E,Object> map;
如有错误,欢迎指正。