day27
集合框架
标绿已经学习底层,深入底层主要是研究实现类底层
手撕HashMap底层源码
JDK1.7版本的HashMap
切换版本
原因:jdk1.7和jdk1.8的HashMap不同(头插法/尾插法)
首先如果没有jdkjre1.7,就安装jdkjre1.7,之后eclipse中添加
由于前期都用的jdk1.8版本,所有要再切换jdk1.7版本
场景:
HashMap<Student, String> map = new HashMap<>();
map.put(new Student("小小", '男', 23, "2401", "001"), "拍电影");
map.put(new Student("大大", '男', 20, "2401", "002"), "打篮球");
map.put(new Student("奇男子", '男', 21, "2401", "003"), "玩游戏");
map.put(new Student("奇男子", '男', 21, "2401", "003"), "写代码");
map.put(null, "aaa");
map.put(null, "bbb");
遍历:数组从头到尾遍历
null=bbb
奇男子 男 21 2401 003=写代码
大大 男 20 2401 002=打篮球
小小 男 23 2401 001=拍电影
补充:NaN - Not a Number
// Float float1 = new Float("0.0f");
// Float float2 = new Float("0.0f");
// Float result = float1/float2;
// System.out.println(result);//NaN
// System.out.println(Float.isNaN(result));
// HashMap<Student, String> map = new HashMap<>(16,result);
底层
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>{
//默认初始化容量 -- 必须是2的幂
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认的负载因子(静态属性共享,也可以添加自定义负载因子)
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//空内容的数组
static final Entry<?,?>[] EMPTY_TABLE = {};
//hash数组/hash表
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;//EMPTY_TABLE
//元素个数
transient int size;//0
//阈值(数组长度*负载因子)
int threshold;//16
//负载因子
final float loadFactor;//0.75f
//外部操作数(记录添加、删除的次数)
transient int modCount;//0
//hash种子数
transient int hashSeed = 0;//0
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
//initialCapacity - 16
//loadFactor - 0.75f
public HashMap(int initialCapacity, float loadFactor) {
//判断数组初始化容量如果小于0,就报错
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
//判断数组容量大于最大容量,就把最大容量赋值给初始化容量
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//判断负载因子如果小于等于0 或者 判断负载因子不是一个数字,就报错
if (loadFactor <= 0 || Float.isNaN(loadFactor))//NaN - Not a Number
throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
this.loadFactor = loadFactor;
threshold = initialCapacity;
init();//作用:让子类去重写(LinkedHashMap),子类做初始化功能
}
void init() {
}
//映射关系类/节点类
static class Entry<K,V> implements Map.Entry<K,V> {
final K key; --------- key
V value; ------------- value
Entry<K,V> next; ----- 下一个节点的地址
int hash; ------------ key的hash值
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
}
}
HashMap理解图
init();的作用
让子类去重写(LinkedHashMap),子类做初始化功能
伪代码理解:LinkedHashMap调用父类的有参构造,int()返过来调用子类LinkedHashMap中重写的int();
面试题
JDK1.7版本的HashMap是什么数据结构?
一维数组+单向链表
什么是Hash桶?
hash数组里的单向链表
什么是hash碰撞/hash冲突?
key的hash值一致,在数组中的下标上有重复的元素
HashMap默认数组长度是多少?
长度是1<<4,就是16的长度
HashMap数组的长度为什么必须是2的幂?
??????(涉及后面线程安全内容先搁置)
HashMap数组的最大容量是多少?
1<<30
HashMap数组的最大容量为什么是1<<30?
最大容量为int类型,int类型的最大值是2的31次方-1
因为HashMap数组必须是2的幂,1<<30是int取值范围内最大的2的幂的数字
所以HashMap数组最大容量是1<<30
HashMap默认负载因子是多少?
0.75f
HashMap的负载因子的作用是什么?
数组长度*负载因子 等于 阈值,阈值是控制何时扩容
HashMap数组默认的负载因子为什么是0.75f?
取得了空间和时间的平衡
如果负载因子过大(如:1),会导致数组全部装满后,再扩容。利用了空间,浪费了时间
如果负载因子过小(如:0.2),会导致数组装了一点点元素,就扩容。利用了时间,浪费了空间