java面试题-常见集合

常见集合

List相关面试题

数组概述

数组是一种用连续的的内存空间存储相同数据类型数据的线性数据结构

数组寻址公式:baseAddress(数组第一个元素的首地址)+ i(数组下标) * dataTypeSize(数据类型大小),如果数组从1开始,要多一步减1的操作

数组的时间复杂度:通过下标查找时间复杂度O(1),未知下标寻找元素的时间复杂度0(n),未知下标通过二分查找的时间复杂度O(logn)

插入和删除的时间复杂度是O(n)

ArrayList底层的实现原理?

arrayList底层是通过动态的数组实现的,ArrayList的初始容量为0,当第一次添加元素的时候,才会把初始容量为10,在添加元素的时候确保数据能够存下下一个元素,如果size+1之后大于当前数组容量,则需要调用grow方法进行扩容,arrayList除了第一次每一次扩容都是原来的1.5倍,确保新增的元素有地方存储之后,将元素放到size的位置上,返回true给add方法

ArrayList list=new ArrayList(10)中的list扩容几次

扩容0次,ArrayList会直接创建一个长度为10的数组

如何实现List和Array之间的转换

List转Array:可以使用list对象的toArray方法将List转为Array

Array转List:可以使用Arrays工具类的asList方法

用Arrays.asList转List之后,如果修改数组的内容,list会受影响吗?

会受影响的,因为使用Arrays.asList转List的底层使用的是Arrays的内部类ArrayList来构造的List,这个ArrayList的构造函数就是将当前数组的应用赋值给了ArrayList的内部数组,实际上他们指向的是同一个内存地址

用List的toArray方法转array之后,如果修改list的内容,array会受影响吗?

不会受影响的,因为当使用List的toArray方法底层是对数据进行的拷贝,他们没有指向同一个内存地址,所以两个数组是没有关系的,即使list修改了内容,array也不会受影响

单向链表和双向链表的区别

单向链表只有一个next指针和数据,双向链表有next和pred两个指针赫尔数据分别指向前一个节点和后一个节点

单向链表的查询的时间复杂度是O(n),查询头是O(1),新增和删除的时间复杂度是O(n),新增和删除头是O(1)

双向链表的查询的时间复杂度是O(n),查询头和尾是O(1),新增和删除的时间复杂度是O(n),新增和删除头和尾是O(1)

ArrayList和LinkedList有什么区别

1、ArrayList底层是采用数组结构,LinkedList底层是采用双向链表结构

2、ArrayList可以按照下标查询,所以查询的时间复杂度是O(1),LinkedList不可以按照下标查询,需要遍历链表,所以查询的复杂度是O(n)

3、ArrayList和LinkedList查找未知元素的下标时间复杂度都是O(n)

4、ArrayList新增和删除的元素后面的元素需要向后移位,所以时间复杂度是O(n),LinkedList新增和删除需要进行遍历,所以时间复杂度也是O(n)

5、ArrayList底层是数组,连续的内存空间,更节约内存,LinkedList底层是链表,每个节点需要格外存储两个指针,所以更占用内存

6、ArrayList和LinkedList都不是线程安全的,局部变量使用可以保证线程安全,也可以使用Collections.synchronizedList来得到线程安全的list

HashMap的实现原理

HashMap的底层是通过数组加链表或者红黑树实现的

hashMap在put元素时,会计算key的hash码,如果key的hash码相同,此时会有两种情况,一种是key相同的情况,会直接覆盖原值,一种是key不相同的情况,会将当前key插入到链表或者红黑树中,当链表的长度大于8 并且元素个数大于64时会将链表转换为红黑树

hashMap获取元素时,会根据key计算出hash值,找到对应的数组小标,在进一步获取出元素

HashMap的jdk1.7和jdk1.8有什么不同?

jdk1.7是采用的拉链法,也就是数组加链表的方式实现的

jdk1.8是采用的是数组+链表+红黑树实现的,当链表长度大于8 并且hashMap的元素个数大于64会将链表转换为红黑树

HashMap的put方法具体流程?

1、先判断hash表(table)是不是为空,如果为空则通过resize方法进行初始化

2、对key的hash值对数组长度取模运算得到数组下标(i),如果table[i]为空,则直接插入值

3、否则table[i]不为空,则为出现如下3种情况

​ 3.1、如果key的值和table[i]的值相等,则直接用key进行替换table[i]

​ 3.2、如果table[i]是红黑数,则将key插入到红黑树中,如果遇到相同的key则可以直接替换

​ 3.3、否则table[i]是链表,hashMap会遍历整个链表,会将当前key插入到链表的尾部,如果链表长度超过8,并且数组元素个数超过64则会将链表转换为红黑树,在插入过程中,如果遇到相同的key,则直接替换该元素就可以了

4、最后会判断元素个数是不是达到的扩容阀值,如果达到扩容阀值会进行扩容,扩容阀值大小,是用当前容量*扩容因子0.75

HashMap的扩容机制

在添加元素或者初始化的时候需要调用resize方法进行扩容,第一次扩容会把数组初始化容量为16,后面每次扩容都是以前长度的2倍,扩容阀值是当前容量的0.75倍,一旦到达这个阀值就会进行扩容

扩容时,会创建一个新的数组,会将老的数组挪到新的数组里面去

​ 如果没有hash冲突的节点会直接使用key的hash值和新数组的容量做与运算计算得到新的索引下标,插入到新的数组中

​ 如果是红黑树,走红黑树的添加逻辑

​ 如果是链表,需要遍历整个链表,可能需要对链表进行拆分,判断key的hash值 & 旧数组的容量是否为0,如果为0则停留在原位置,如果是1则存到需要当前位置加上旧数组的容量的位置,

因为如果是0的话,证明当前最高位对应的key的hash值也是0,那么计算出的下标其实也是一样的,如果是1 的话,证明当前最高位对应的key的hash值也是1,那么计算出的下标就相当于旧数组的容量+当前下标

详细解释链接:https://blog.csdn.net/qq_45369827/article/details/114960370

HashMap的寻址算法

hashMap通过hashCode方法计算出hash值,在将hash值向右移16位,在对hash值进行异或运算,这样能够让hash分布更均匀,最后对数组取余运算,得到索引

为何HashMap的数组长度一定是2的n次幂

计算索引效率更高,可以用与运算代替取模运算

在扩容的时候重新计算索引效率更高, hash & oldCap == 0 的元素留在原来位置 ,否则新位置 = 旧位置 + oldCap

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值