包装类型常量池技术
Byte
,Short
,Integer
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character
创建了数值在 [0,127] 范围的缓存数据,Boolean
直接返回 True
or False
。
如果有一个int和一个Integer他们之间可以用==比较吗?
理论上来说因为有包装类的常量池技术,Integer类型的数据如果处于[-128,127] 之间,则可以直接使用==进行判断,但是如果在这个区间之外的数据会在堆上产生,要使用equals方法进行判断
静态变量和静态代码块的执行顺序
类的加载过程分为,加载阶段,链接阶段,初始化阶段。
而链接阶段又分为:验证阶段,准备阶段,解析阶段; 其中在准备阶段是为类变量分配内存和赋初值
所以Java中静态变量和静态代码块是在类加载的过程中就执行的,实例化对象时先实例化变量再执行构造函数
代码和代码块的执行顺序和代码书写顺序一致
代码的初始化过程为:
-
初始化父类中的静态成员变量和静态代码块 ;
-
初始化子类中的静态成员变量和静态代码块 ;
-
初始化父类的普通成员变量和代码块,再执行父类的构造方法;
-
初始化子类的普通成员变量和代码块,再执行子类的构造方法;
HashMap 的底层实现
Map结构的理解
Map中key :无序,不可重复,使用set存储, key所在的类重写equals()和hashcode()
Map中的value:无序的,可重复的,使用Collection存储所有的value, Value所在类重写equals()
key-value =Entry对象:无序的,不可重复的,使用set存储所有的entry。
HashMap
底层是 数组和链表 结合在一起使用, HashMap 通过key的hashCode经过扰动函数处理后得到hash值后判断当前元素存放的位置,若当前位置已有元素就要判断与存入元素的hash值和主键是否相同,相同的直接覆盖,不同的用拉链法解决冲突。
为什么默认初始容量为2次幂?
如果 n 为 2次幂,可以保证数据的均匀插入,降低哈希冲突的概率
在putVal()
原码中
// 如果没有hash碰撞则直接插入元素
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
......
}
i = (n - 1) & hash :n是map的容量, 任何2的N次幂-1以后 二进制位上都是1,
n-1与hash做与运算,得到地位的hash值这样发生哈希碰撞的概率更低
HashMap 中的扰动函数是一个通过对 key 值类型自带的哈希函数生成的散列值进行位移计算来扰乱散列值, 以达到降低哈希碰撞的概率的方法
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
因为 int 类型是 32 位, 但是很少会有数组这么大,(n-1)通常在低位上才有取值,那当(n - 1) & hash时 即使高位不同只有低位相同也会发生hash碰撞,为此 将 hash 值右移16位(hash值的高16位)与 原 hash 值做异或运算(^),从而得到一个新的散列值(同时使用高位信息和低位信息) 减少hash碰撞的概率
hash()
主要是 哈希算法 和 冲突的解决
static final int hash(Object key) {
if (key == null){
return 0;
}
int h;
h = key.hashCode();返回散列值也就是hashcode
// ^ :按位异或
// >>>:无符号右移,忽略符号位,空位都以0补齐
//其中n是数组的长度,即Map的数组部分初始化长度
return (n-1)&(h ^ (h >>> 16));
}
JDK8相较于7中实现方面的不同:
-
new HashMap()底层没有创建一个长度为16的数组 只赋值了负载因子
-
jdk8底层数组是:Node[],而不是entry
-
首次使用put方法时底层创建长度为16的数组
-
jdk7底层结构只有:数组+链表,8中数组+链表+红黑树
-
当链表形式存在个数>8且当前数组长度>64时 此索引上所有数据改为使用红黑树存储
HashMap get()源码
public V get(Object key) {
Node<K,V> e;
return (e = getNode(key)) == null ? null : e.value;
}
/**
* Implements Map.get and related methods.
*
* @param key the key
* @return the node, or null if none
*/
final Node<K,V> getNode(Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n, hash; K k;
//table不为空 且table长度不为0且 tab[hashindex]不为空
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & (hash = hash(key))]) != null) {
if (first.hash == hash && //检测头结点与 所求key的hash是否相等
//且头结点的key值是否和key值相等, 或key不为空且key与头结点相等
((k = first.key) == key || (key != null && key.equals(k))))
return first;//直接返回头结点
if ((e = first.next) != null) {//头结点下有链表
if (first instanceof TreeNode)//instanceof 左边对象是否为instanceof 右边类的实例
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}