java集合
Collection 单列集合
Map 双列集合
时间复杂度分析:常对幂指阶
代码执行时间不随n的增大而增大,时间复杂度为O(1)
数组:连续内存空间 相同数据类型
数组索引从0开始
寻址公式:数组首地址+索引*元素类型大小
若从1开始,公式中需要增加一次减法操作,cpu多了一次指令
ArrayLIst
动态的数组实现,占用连续内存
初始容量为0,第一次添加数据初始化为10
每次扩容为原来的1.5倍
添加
size+1是否满足
不满足grow扩容为原来的1.5倍
添加元素到size的位置
返回添加成功boolean值
ArrayList list=new ArrayList(10)中的list扩容0次
声明和实例,指定容量为10,未扩容
数组和List之间的转换
数组转LIst Arrays.asList
List转数组 List.toArray
数组转List,修改数组内容List受影响
底层使用Arrays类中的一个内部类ArraysList构造的集合
对传入数组进行包装,最终指向同一片内存
List转数组,修改List内容数组不受影响
底层进行了数组的拷贝
LinkedList
双向链表实现,头尾指针都有,内存利用率低
ArrayList和LinkedList
线程不安全
在方法内部使用,局部变量是线程安全的
使用线程安全的ArrayList和LinkedList
List
二叉树:数组存储 链式存储
二叉搜索树 二叉查找树 有序二叉树 排序二叉树
最坏情况,二叉查找树退化成链表,查找时间复杂度O(n)
红黑树
左根右 根叶黑 不红红 黑路同
HashMap
散列表+红黑树+链表
散列表(hash表)根据键直接访问在内存存储位置值的数据结构 数组 随机访问
散列冲突,哈希碰撞,哈希冲突 多个key映射到同一个数组下标
散列表每个下标位置称作桶或者槽,每个桶对应一个链表,散列值相同放到同一个链表中
平均情况下拉链法查找时间复杂度为O(1)
最坏退化为链表O(n),故引入红黑树,查询时间为O(logn)
DDOS攻击
分布式拒绝服务攻击(攻击的发出点分布在不同位置)
不同位置的多个攻击者同时向一个或数个目标发动攻击
HashMap实现原理
底层hash表,数组+链表/红黑树
put元素:利用key的hashCode重新hash计算当前元素数组中的下标
存储hash值相同的key
key相同覆盖,key不同,即冲突添加
链表长度大于8且数组长度大于64转换为红黑树
扩容resize,红黑树拆分的树节点小于等于6,退化为链表
JDK1.7之前使用拉链法
扩容阈值=数组容量*加载因子
HashMap时懒惰加载,创建对象没有初始化数组
默认加载因子0.75
put方法具体流程
1.判断数组table是否为空或者NULL,是则执行resize()扩容(初始化)
2.根据键值key计算hash得到数组索引
3.table[i]==null 直接新建节点添加
4.table[i]!=null
4.1 判断首个元素key是否相同,同则覆盖
4.2 是否为红黑树,是则插入红黑树中,发现key存在,则覆盖
4.3 遍历链表,有相同key覆盖,没有则进行尾插,链表长度大于8转换成红黑树
5.插入成功后,判断所有键值对的数量size是否超过了最大容量(数组长度*0.75),
超过扩容,注意这里和一共使用了多少个桶无关,只要总数超了就扩
扩容机制
初始化需要调用resize扩容,第一次添加数据初始化长度为16
扩容阈值 数组长度*0.75
每次扩容都是扩容之前的两倍
扩容是创建新数组,老数组数据挪到新数组中
没有hash冲突的节点,e.hash&(newCap-1)计算新的索引位置
红黑树的节点会被拆分为两个链表,超8变树
链表 拆分链表 判断e.hash&oldCap是否为0,
0为原始位置,1为原始位置+增加的数组大小
寻址算法
扰动算法,hashcode高16位和低16位异或,使hash值更加均匀,减少hash冲突
(n-1)&hash 得到数组中的索引,代替取模,性能更好,数组长度必须是2的n次幂
HashMap的数组长度一定是2的次幂
(n-1)&hash 得到数组中的索引,代替取模,性能更好
扩容时重新计算索引效率更高
hash&oldCap==0的元素留到原位置,否则新位置=旧位置+oldCap
hashMap多线程下死循环(数组+链表)
数组扩容使用头插法,数据迁移导致死循环
两个线程同时扩容的时候会出现这个问题