提示:本文章是基于jdk1.7,对于一些常见类底层学习的公开笔记,学艺不精,发现错误请评论提出。
一:实现的接口,继承的类
继承1:AbstractMap:是一个抽象类,他实现了Map接口。对Map接口的一些方法进行了实现。
2:Map<K,V>:是一个接口,规范map集合需要实现的方法
3:Cloneable:克隆接口,无任何方法参数,但却实现了一个隐藏机制,无需调用构造器就可以创建对象的克隆机制。
4:Serializable:序列化
二:拥有的属性
1
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
注:默认初始化容量属性,意味着HashMap的默认初始化容量为16
2
static final int MAXIMUM_CAPACITY = 1 << 30;
注:最大容量:1 073 741 824
3
static final float DEFAULT_LOAD_FACTOR = 0.75f;
注:存储因子具体值。存储因子越大,对系统空间利用率越高,但越容易因为hash冲突,反之亦然。因此取了一个中间值。
4
static final Entry<?,?>[] EMPTY_TABLE = {};
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
注:当新增等操作会用到,日后附带代码说明,jdk标明的注释,依旧解释不清晰
5
transient int size;
int threshold;
注:长度,一个可以被序列化,一个不可以,后者是hashmap空间的长度,前者则是数据的长度。
6
final float loadFactor;//存储因子
7
transient int modCount;
注:改值记录了这个对象被操作的次数。即使map执行了clear方法,你个数值也不会被清零。假如克隆这个对象而新生成的对象,那么新对象的这个属性会是0;
8
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;
译:可替换的代码值。也就是map里链表的最大个数。
9
//内部类 没有太高深的逻辑,底层也进入到了c语言,所以就做个简单的分析
//保存VM启动后无法初始化的值。
private static class Holder {
static final int ALTERNATIVE_HASHING_THRESHOLD;
static {
String altThreshold = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"jdk.map.althashing.threshold"));
//初始化的时候int默认为0,altThreshold也是0
int threshold;
try {
threshold = (null != altThreshold)
? Integer.parseInt(altThreshold)
: ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;
//显然doPrivileged方法并不能保证一定会读出来这个0的数据
// disable alternative hashing if -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;
}
}
注:那么这个内部类的意义是什么?其实 没什么高深的东西,就类似读取配置文件,初始化这个工具属性。当长度发生改变时或者克隆都会用到。具体初始化了什么还真不知道
10
我决定把这个内部类提前,因为这个比较重要,不先写出来梳理的不顺畅
//HashMap的核心这个表明了HashMap的存储方式
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
//所说的链表,不过是一个大脑虚拟的,方便理解的一种形式
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;
}
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;
}
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) {
}
}
注:这虽然是核心,但却没什么特殊的东西,没有什么高深的逻辑和技术。
当执行put方法是,其实就是在数组的下一个索引上增加一个这个对象,next就是存储的下一个这个对象。
具体的实现会慢慢以展现代码梳理。
2018年11月14日10:00:00,第一次更新。边工作边看底层,更新速度不会很快
2018年11月15日15:19:39,第二次更新