目录
- 前置知识
- ==和equals的区别
- 为什么要重写equals和HashCode
- 时间复杂度
- (不带符号右移) >>>
- ^异或运算
- &(与运算)
- 位移操作:1<<2=4,1左移两位为什么等于4
- HashMap集合特点及源码分析(JDK1.8)
- HashMap重要的五大点
- 1.集合初始化
- HashMap成员变量
- HashMap内部数据结构
- 链表
- 红黑树
- HashMap构造方法
- 指定初始容量
- 指定填充比
- 用来初始化的Map
- 2.数据寻址Get
- 3.数据存储Put
- 4.节点删除
- 5.扩容原理
- JDK1.7 HashMap中扩容机制resize()
- 多线程环境下扩容造成死循环的分析过程
- 解决办法
- JDK1.8 HashMap中扩容机制resize()
- JDK1.7 HashMap中扩容机制resize()
- 1.集合初始化
- HashMap重要的五大点
- HashMap面试题
- HashMap什么时候会转换为红黑树
- HashMap为什么要引进红黑树,为了解决什么问题?
- HashMap的长度为什么必须是2的次幂?
- 如何避免HashMap内存溢出问题
- HashMap根据key查询的时间复杂度
- HashMapKey为null存放在什么位置
- HashMap底层是采用单链表还是双链表
- HashMap底层是有序存放的吗
- LinkedHashMap 和 TreeMap底层如何实现有序的
- 为什么HashMap不使用取模运算
- 求下标i=(n-1)&hash,为什么(n-1)变成了奇数
- HashMap如何降低Hash冲突
- 加载因子为什么是0.75而不是1
- Hashap存放1W条数据怎么样效率最高
- Hashmap1.7和Hashmap1.8的区别
前置知识#
==和equals的区别#
对于基本类型变量来说,只能使用 == ,因为基本类型的变量没有方法。使用==比较是值比较
对于引用类型的变量来说,==比较的两个引用对象的地址是否相等。所有类都是继承objcet类,而object类是equals方法比较的也是对象的地址是否相等,如果类没有重写equals方法,使用 == 和equals方法效果是一样的
为什么要重写equals和HashCode#
HashCode方法:底层采用C语言编写,根据对象地址转换为整数类型
如果两个对象的HashCode相等,对象的内容至不一定相等;hash碰撞的问题
如果使用equals方法比较两个对象内容值相等的情况下,那么hashcode的值也相等
因为equals默认情况下Object类采用==比较对象,那么比较的是内存地址是否相等,当数据类型只要不是基本类型,那么比较永远不会相等。
set集合存储的就是不重复的对象,底层就是hashmap,依据equals和hashcode进行判断
时间复杂度#
时间复杂度为O(n) 从头查询到尾部,查询多次
时间复杂度为O(1) 查询一次 比如根据数组下标查询
时间复杂度为O(logn) 平方查询 比如红黑树,
效率:O(1)>O(logn)>O(n)
(不带符号右移) >>>#
无符号右移就是右移之后,无论该数为正还是为负,右移之后左边都是补上0
无符号右移运算符和右移运算符的主要区别在于负数的计算,因为无符号右移是高位补0,移多少位补多少个0
15>>>2=0000 1111 右移两位=0000 0011=3
^异或运算#
相同为0,不同为1
2^3= 0010^0011=0001=1
&(与运算)#
00得0 11得1 01得0
2&3=0010&0011=0010=2
位移操作:1<<2=4,1左移两位为什么等于4#
这里的1是十进制,而计算机交流是用二进制,所以先要将1用二进制表示出来。
每一个符号(英文、数字或符号等)都会占用1Bytes的记录,每一个中文占2Byte
而一个1Bytes占8个bit,也就是8个二进制位
8位二进制数:28种不同状态 0000 0000 ~1111 1111=0~255=28=256
1的二进制表示0000 0001,然后进行位移操作。
位移操作向左边位移,后面空出来的补上0,越往左边越大,把0000 0001向左位移2位,变成了0000 0100,二进制0000 0100转化十进制所以为4,也可以说每左移一位是乘以2
8>>2?
0000 1000右移2位0000 0010,转化为10进制等于2
10>>2?
0000 1010右移2位0000 0010,转化为10进制等于2
1<<30?
0000 0001左移30位01000000 00000000 00000000 00000000,转化为10进制等于1073741824,也就是230
HashMap集合特点及源码分析(JDK1.8)#
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
HashMap继承了AbstractMap类,实现了Cloneable克隆接口、Serializable序列化接口、Map接口
特点:数组+链表+红黑树构成
HashMap重要的五大点#
1.集合初始化#
HashMap成员变量#
//默认初始化hashmap容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //2的4次幂 16
//hashmap最大容量1073741824
static final int MAXIMUM_CAPACITY = 1 << 30;//2的30次幂
//扩容因子 16*0.75=12 达到12就会进行扩容
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//链表中存储元素的数量 > 8 时,会自动转换为红黑树
static final int TREEIFY_THRESHOLD = 8;
//删除元素时,如果一个红黑树中中存储元素数量 < 6 后,会自动转换为链表
static final int UNTREEIFY_THRESHOLD = 6;
//数组容量>64&链表长度>8 转为红黑树
static final int MIN_TREEIFY_CAPACITY = 64;
//阙值,用于判断是否扩容,threshold=容量*扩容因子=16*0.75=12
int threshold;
//扩容因子实际大小
final float loadFactor;
//HashMap中元素的数量 transient表示不能被序列化
transient int size;
//集合修改次数 防止多线程篡改数据
transient int modCount;
//存储元素的数组 单向链表
transient Node<K,V>[] table;
HashMap内部数据结构#
链表
//单向链表 实现了Entry接口 由上面的数组构成了数组加链表的结构
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
//构造一个节点
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
//基本方法
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
//比较两个Node是否相等
public final boolean equals(Object o) {