Java集合常见面试题

这篇文章要包括的问题

  • 1、Arraylist的 add 方法是怎样的
  • 2、HashMap是怎么实现的
  • 3、HashMap与ConcurrentHashMap的性能比较
  • 4、HashMap解决hash冲突的方法
  • 5、 常见解决hash冲突的方法
  • 6、 为什么HashMap 长度为2的幂次方
  • 7、HashMap 扰动函数作用
  • 8、红黑树

Arraylist的 add 方法是怎样的

添加元素时使用 ensureCapacityInternal() 方法来保证容量足够,如果不够时,需要使用 grow() 方法进行扩容,新容量的大小为 旧容量的 1.5 倍。
扩容操作需要调用 Arrays.copyOf() 把原数组整个复制到新数组中,这个操作代价很高,因此最好在创建ArrayList 对象时就指定大概的容量大小,减少扩容操作的次数

HashMap是怎么实现的

1、存储结构(数组链表)
transient Entry[] table;

2、put操作
根据 key 值计算出 hashCode 值,根据 hashCode 值 与 hash 数组长度取余得到桶的下标,如果是key 是 null值,则放在第0个桶中
在对应的的桶链表中查找是否已存在该key, 如果存在则更新,否则执行插入操作
插入操作先判断是否需要扩容,如果需要扩容,则扩容为原来的2倍,把原来的键值对都拷贝到新的数组中,然后执行插入操作

3、查找操作
计算键值对所在的桶;
在链表上顺序查找,时间复杂度显然和链表的长度成正比。

4、扩容(2倍)
当往 HashMap中添加元素时,hashMap 会根据 当前数组容量大小,键值对数量,以及装填因子来衡量空间效率和时间效率,如果觉得有必要扩容,HashMap调用 resize()函数进行扩容为原来的两倍,过程中需要调用 transfer() 函数将原 table 中的数据拷贝到新的数组中,这是很耗费时间的。

5、 链表转换成红黑树
JDK1.8 以后,当数组大小已经超过64并且链表中的元素个数超过默认设定(8个)时,将链表转化为红黑树

3、HashMap与ConcurrentHashMap的性能比较

ConcurrentHashMap 基本介绍
1、 jdk1.7:底层数据结构是数组加链表。和hashmap不同的是ConcurrentHashMap中存放的数据是一段段的,即由多个Segment(段)组成的。每个Segment中都有着类似于数组加链表的结构。
其中 Segment 的个数控制了并发级别,在一个ConcurrentHashMap创建后Segment的个数是不能变的,扩容过程过改变的是每个Segment的大小。
2、在HashMap 的基础上对每个桶加锁的形式实现的,取代了原来 segment + ReetrantLock 的形式。
3、为什么改进?
分段锁的缺点是 在于分成很多段时会比较浪费内存空间(不连续,碎片化); 操作map时竞争同一个分段锁的概率非常小时,分段锁反而会造成更新等操作的长时间等待(因为加锁操作会涉及到用户态和内核态的切换); Segment的个数限制了并发级别,且随着段的增大,锁的粒度在增大,分段锁的性能会下降,但是 改成 CAS + synchroinzed 后,仍是一个完整的数组,不会有碎片化的问题,而且桶的个数代表了并发度,并发级别也比原来的高。至于对每个桶进行加锁,选择的是 synchronized 而不是 reetrantLock, 这是因为在锁的粒度比较小的情况下,经过优化的synchronized 会比 ReetrantLock 有更好的性能。因为锁的粒度比较小的情况下,如果使用ReentrantLock则需要节点继承AQS来获得同步支持,增加内存开销,而synchronized则是JVM原生支持的,内存开销较小

4、HashMap解决hash冲突的方法

链表头插法(JDK1.7之前)
链表尾插法(JDK1.8及JDK1.8之后)

5、常见解决hash冲突的方法

链表法和开放地址法。
(1)链表法就是将相同hash值的对象组织成一个链表放在hash值对应的槽位;
开放地址法是通过一个探测算法,当某个槽位已经被占据的情况下继续查找下一个可以使用的槽位。
(2)开放地址法又分为以下三种:
线性探测:当发现冲突后,就尝试放在下个位置,如果下个位置还是冲突则再尝试下个位置,一直到不冲突为止
二次探测:当发现冲突后,就尝试放在当前位置往后1平方的位置,如果下个位置还是冲突则尝试放在往后2平方的位置,直到不冲突为止
双散列:也成为再哈希法,如果发生冲突,这个位置增量是通过一个哈希函数计算出来的
值得提醒的是,对利用开放地址法查了冲突所产生的哈希表中删除一个元素,不能简单地直接删除,因为这样将截断其它具有相同哈希地址的元素的查找地址,所以应设定一个特殊的标志以表明该元素已被删除。
参考:开放地址法

6、 为什么HashMap 长度为2的幂次方

通常,hash表通过 【取模操作】 确定元素位置
在数组长度 = 2的幂次方条件下,【取模操作 等价 hash & (n-1)】。而位操作效率更高。
为的是确保n-1的二进制数全部是1,好用来做&运算取到长度范围之内的位。

7、HashMap 扰动函数作用

减少扰动

8、红黑树

红黑树的性质

1.每个结点要么是红的要么是黑的。
2.根结点是黑的。
3.每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
4.如果一个结点是红的,那么它的两个儿子都是黑的。
5.任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

这五个性质强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。为什么呢?性质4暗示着任何一个简单路径上不能有两个毗连的红色节点,这样,最短的可能路径全是黑色节点,最长的可能路径有交替的红色和黑色节点。同时根据性质5知道:所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。
一颗有n个内结点的红黑树的高度至多为2lg(n+1).

红黑树与二叉查找树的性能比较

由于二叉树对某个元素的搜索是与该元素的距离树根结点的高度来决定的,而红黑树的高度不会超过2lg(n+1),因此可以在O(logn)时间内做查找,插入和删除,时间非常快,而二叉查找树通常情况不是一个平衡的二叉树,最坏情况下,树的高度可以达到n,因此查找的时间为O(n)。
红黑树和平衡二叉树的优势的插入删除维护平衡旋转的次数更少

既然红黑树那么好,为啥hashmap不直接采用红黑树,而是当大于8个的时候才转换红黑树?

因为数据量小的时候往往复杂度高的算法效率高,比如在数据量比较小时,插入排序效率就比快速排序的效率高。
红黑树需要进行左旋,右旋操作,有一定的维护和建立开销, 而单链表不需要,
以下都是单链表与红黑树结构对比。
如果元素小于8个,查询成本高,新增成本低
如果元素大于8个,查询成本低,新增成本高

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值