【集合呀】

ArrayList的自动扩容机制

ArrayList是一个数组结构的存储容器 默认数组的长度为10 当构建的ArrayList对象

随着程序不断的往里面添加数据 当添加数据达到10个的时候ArrayList里面

就没有足够的容量去存储后续的数据 此时 ArrayList就会会触发自动扩容 其有以下几步

首先:创建一个新的数组 其长度是原数组长度的1.5倍

然后:使用Arrays.copyOf方法把原数组里面的数据拷贝到新的数组里面

最后:在扩容完成以后再把当前需要添加的元素 加入新的数组里面 从而去完成动态扩容这样一个过程

HashMap和HashTable的区别

从功能特性的角度来说有两个不同

1.HashTable是线程安全的 而HashMap不是

2.HashMap的性能要比HashTable更好,因为HashTable采用了全局同步锁来保证安全性,对性能影响较大

从内部实现的角度来说有一下几点

1.HashTable使用数组和链表,HashMap采用了数组+链表+红黑树

2.HashMap初始化容量是16 HashTable初始化容量是11

3.HashMap可以使用null作为key而Hashtable不允许

最后 他们两个的key的散列算法不同,HashTable直接使用key的hashCode对数组长度取模,

而HashMap对key的hashCode做了二次散列,从而避免key的分布不均匀问题影响到查询性能

ArrayList与LinketList有什么区别

主要有以下几点

第一个内部实现方式 ArrayList是数组 通过索引来访问元素,支持快速随机访问

LinkedList是双向链表 前后通过指针相连适合插入和删除操作

第二数据访问的时间复杂度不同 ArrayList访问的时间复杂度为O(1)

LinkedList需要从头部或者尾部开始遍历链表 直到找到目标元素的位置因此它的时间复杂度是一个o(n)

第三空间占用 ArrayList使用数组来存储数据 占用的空间是连续的 可能会存在内存碎片

LinkedList通过链表来连接元素 每个元素都包含前后节点的引用 占用的空间会比较大

HashMap是如何扩容的

第一个:当hashMap中的元素个数超过临界值的时候就会自动触发扩容

临界值有一个公式 它等于负载因子*容量大小 负载因子默认是0.75 容量默认值是16

也就是说元素个数达到12的时候会触发扩容

第二个:它扩容的大小是原来的两倍 由于动态扩容这个机制存在

因此在实际应用里面需要注意 集合初始化的时候明确去指定集合的大小 避免频繁扩容带来性能上的影响

假设我们向HashMap里面存1024个元素 如果按照默认的值是16的情况下 随着元素的不断增加 会造成至少7次扩容

而这7次扩容 需要去重新创建新的Hash表 并且进行数据的一个迁移 对性能的影响是非常大的

扩容因子表示Hash 表中的填充程度 扩容因子的值越大 那么与围着扩容的元素个数会更多 虽然它的整体空间利用率比较高

但是Hash冲突的概率也会增加 反过来说 扩容因子的值越少 那么触发扩容元素的个数也就越小 那么意味着Hash冲头的概率也会减少

但是对于内存空间的浪费就比较多了 而且呢还会增加扩容的频率

因此扩容值的设置本质上就是一个冲突的概率 以及空间利用率之间的一个平衡 0.75这个值的来源 和统计学里面的泊松分布有关

我们知道hashMap里面是采用链式寻址的方式来去解决Hash冲突问题 而为了避免链表过长 带来的一个时间复杂度增加 情况

所以链表长度大于等于7的时候 就会转化为红黑树 从而去提升解锁的效率

当扩容因子在0.75的时候 链表长度达到8的可能性几乎为0

也就是说比较好的达到了一个空间成本 和时间成本的一个平衡

HashMap如何解决哈希冲突

主要有以下几点

第一Hash冲突 首先我们需要了解Hash算法和Hash表 hash算法 就是把任意长度的输入 通过散列算法变成固定长度的输出

这个输出结果就是一个散列值

第二 Hash表又叫做 ”散列表 ”他是通过key直接访问到 内存存储位置的数据结构

在具体的实现上通过Hash函数把key映射到表中的某个位置 来获取这个位置的数据 从而去加快数据的查找

第二所谓的hash冲突 是由于哈希算法 被计算的数据是无限的 而计算后的结果的范围是有限的所以总会存在不同的数据

经过计算之后得到的值是一样 那么这个情况下就会出现所谓的哈希冲突

第三 通常解决Hash冲突的方法有四种

1.开放定址发也成线性探测法 就是从发生冲突的那个位置开始 按照一定次序

从Hash表中去找到一个空闲的位置然后把发生冲突的元素 存入到这个位置 而在Java中 ThreadLocal就用到了线性探测法来解决Hash冲突

第一个元素的通过Hash计算得到的索引是1 第二个元素通过Hash计算得到的索引也是1 那么这个时候我们称为Hash冲突

而开放定制法就是按照顺序 向前去找到一个空闲的位置 来存储这个冲突的key

第二 链式寻址法 这是一种非常常见的一种方法 简单理解就是把存在Hash冲突的key 以单向链表的方式进行存储

比如HashMap就用到了链式寻址法来实现 存在冲突的key直接以单向链表的方式去进行存储

第三 再hash法 就是通过某个Hash函数计算的key 存在冲突的时候再用另外一个Hash函数对这个key进行Hash 一直运算直到不再产生冲突为止

这种方式会增加计算的一个时间 性能上会有一定的影响

第四个 简立公共溢出区 就是把Hash表分为基本表 和溢出表两个部分 凡是存在冲突的元素 一律放到溢出表中

第四个HashMap在JDK1.8版本中 是通过链式寻址法 以及红黑树的方式来解决Hash冲突问题的

其中红黑树是为了优化 Hash表的链表过长导致时间复杂度增加的一个问题当链表长度大于8并且容量大于64的时候

再向链表中添加元素 就会触发链表向红黑树的一个小转化

为什么 ConcurrentHashMap中key不允许为null

concurrentHashMap这么设计的原因 是为了去避免在多线程并发场景下的一个歧义问题

当一个线程从ConcurrentHashMap中去获取key的时候 如果返回的结果是null

那么这个线程 是无法确认这个null 表示的是确实不存在这个key 还是说是存在这个key但是value为空

那么这个不确定性会造成线程安全性问题

而ConcurrentHashMap本身是线程安全的一个集合 所以才有了这样一个设计

HashMap中的hash方法为什么要右移16位异或运算

之所以要对hashCode无符号右移16位 并且异或核心目的是为了让hash值散列度更高 尽可能的去减少hash表的hash冲突 从而去提升数据的查找性能 在hashMap的put方法中是通过key的hash值与数组的长度取模计算 得到数组的位置 而在绝大部分情况下 n的值一般是小于2……16次方 也就是65536所以也就意味着i的值始终是使用hash值的低16位与(n-1)取模运算 这个呢是由于运算符的一个特征决定的 这样就会造成key的散列度不是很高 导致大量的key集中存储在一个固定的几个数组位置上 很显然会影响到数据的查找性能 因此为了提升key的hash值的一个散列度在hash方法里面做了一个位移运算 首先使用key的hashCode无符号右移16位 意味着把hashCode的高位移动到了低位 然后再用hashCode与右移之后的值 进行异或运算 就相当于把高位和低位的特征进行了一个组合 这样通过组合和的一个hashCode 通过与运算符进行运算之后 它得到的一个数组的位置的散列度 一定会更高 所以通过这种方式呢 可以去降低hash冲突的概率

接口继承关系和实现

集合存在的包:
Collection:Collection是集合List、Set、Queue的最基本的接口

Map:是影射表的基础接口

List:有序可重复 共有三个实现类 ArrayList、Vector和LinkedList

ArrayList:存在扩容,。。。其中复制、移动代价比较高,从而更适合查找,不适合插入和删除

Vector(数组实现,线程同步):支持线程同步,即木一时刻只有一个线程能够写Vector,因此访问它比ArrayList更慢

LinkList:适合插入和删除 

Set:无序不重复

HashSet:

TreeSet:

LinkHashSet

Map

HashMap:

HashTable:

TreeMap:

LinkHashMap:

ConcurrentHashMap:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值