java 集合八股

  1. Java集合常见类型?

在这里插入图片描述
collection是单列集合,map是双列集合。collection中有两个子接口,list和set,平时用的比较多的像arraylist、linkedlist。hashset、treeset。
map接口中比较常见的是hashmap、treemap、还有一个线程安全的concurrenthashmap
2. arraylist底层是如何实现的?
其底层是使用动态数组来实现的。其初始容量为0,第一次添加数据的时候才会初始化容量为10。array list在进行扩容的时候是原来容量的1.5倍,每次扩容都需要拷贝数组。
其向array list中添加元素的逻辑为:
第一:确保数组已使用长度(size)+1之后足够存下下一个数据
第二:计算数组的容量,如果当前数组已使用长度+1后大与当前数组的长度,则使用grow方法进行扩容(1.5倍)
第三:确保新增数据有地方存储后,则将新元素添加到size的位置
第四:添加成功返回布尔值

  1. 数组和list之间是如何转换的?
    数组转list可以使用Arrays这个jdk自动的一个工具类,使用Arrays.aslist()方法可以直接转换成list。
    list转数组:可以调用list中toArray()方法,需要给一个参数,指定数组的类型,需要指定数组的长度

  2. 用Arrays.asList转List后,如果修改了数组内容,list受影响吗?List用toArray转数组后,如果修改了List内容,数组受影响吗?
    第一:Arrays.asList转成list之后,如果修改了数组的内容,list会受影响,因为它底层使用的Arrays类中的一个内部类来构造的集合,在这个集合的构造器中,把我们传入的集合进行了包装,但最终都是指向的同一个内存地址。
    第二:List.toArray()转成数组之后,如果修改了list的内容,不会影响数组,因为底层是进行了数组的拷贝,跟原来的元素没有关系了,所以即使list修改,数组也不会受影响。

  3. ArrayList 和 LinkedList 的区别是什么?
    ArrayList底层使用的是动态数组实现的,LinkedList底层使用的是双向链表。
    对于数据的操作效率,ArrayList按照下标查询复杂度是O(1),LinkedList不支持下标查询。未知索引时二者查找效率都是O(n)。
    ArrayList底层是数组,内存连续,节省内存。LinkedList是双向链表需要存储两个指针是数据,更占用内存。
    二者都不是线程安全的。

  4. 如何解决ArrayList和Linked List二者都不是线程安全的问题?
    我们可以优先在方法内使用,定义为局部变量。
    如果要作为成员变量来使用的话,可以使用线程安全的集合来替代。
    arraylist可以使用synchronizedlist方法将其转换成线程安全的容器再使用
    linked list可以转换成concurrent linkedqueue来使用。

  5. hashmap的实现原理?
    1底层采用hash表和红黑树(数组+链表|红黑树)
    2添加数据时,计算key的值确定元素在数组中的下标,key相同则替换,不同则存入链表或者红黑树中。
    3获取数据通过key的hash计算数组下标获取元素

  6. hashmap在jdk1.7和jdk1.8有什么区别?
    1.8之前采用的是拉链法,即数组+链表
    1.8之后采用数组+链表+红黑树,链表长度大于8且数组长度大于64则会从链表转换成红黑树

  7. hashmap的put方法具体流程
    1判断键值对数组table是否为空或null,否则执行resize()进行扩容(初始化)
    2根据键值key计算hash值得到数组索引
    3判断table[i]==null,条件成立,直接新建节点添加
    4如果table[i]==null不成立,
    4.1判断table[i]的首个元素是否和key一样,如果相同则直接覆盖
    4.2判断table[i]是否为treenode,即table[i]是否为红黑树,如果是则直接在树中插入键值对。
    4.3遍历table[i],链表的尾部插入数据,然后判断链表长度是否大于8,大于的话把链表转成红黑树,在红黑树中执行插入操作,遍历过程中若发现key已经存在则直接覆盖value
    5插入成功后,判断实际存在的键值对数量size是否超过了最大容量(数组长度*0.75),如果超过则进行扩容。

  8. hashmap的扩容机制?
    1在添加元素或者初始化的时候调用resize方法进行扩容,第一次初始化数组长度为16,以后每次扩容都是达到了扩容阈值(数组长度*0.75)
    2每次扩容的时候,都是扩容为之前容量的2倍
    3扩容之后会创建一个新的数组,把老数组的数据挪动到新的数组中
    4没有hash冲突的节点,则直接使用e.hash&(newCap-1)计算新数组的索引位置
    5如果是红黑树的话,走红黑树的添加
    6如果是链表,则可能需要拆分链表,判断e.hash&oldCap是否为0,该元素要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上。

  9. hashmap的寻址算法?
    这个哈希方法首先计算出key的hashCode值,然后通过这个hash值右移16位后的二进制进行按位异或运算得到最后的hash值。
    在putValue的方法中,计算数组下标的时候使用hash值与数组长度取模得到下标位置

  10. 为什么hashmap的数组长度一定是2的次幂
    计算索引时效率更高,2的n次幂可以使用位与运算替代取模
    扩容的时候重新计算索引效率更高

  11. hashmap是线程安全的吗?
    不是,如果想要线程安全,可以采用concurrenthashmap

  12. hashset和hashmap的区别?
    hashset底层是使用hashmap实现的,利用hashmap的key进行存储元素值,而value默认位object对象,所以hashset不允许重复。两个元素的hashcode相等并且equals()方法返回true。

  13. hashtable和hashmap区别?
    hashtable是数组+链表
    hashmap在jdk1.8之后是数组+链表+红黑树
    hashtable存储数据不能位null,而hashmap可以。
    hash算法不同,hashtable是用本地修饰的hashcode值
    扩容方式不同,hashtable是当前容量翻倍+1,hashmap是当前容量翻倍
    hashtable是线程安全的,hashmap不是线程安全的,效率更高一些。
    实际开发环境一般不建议使用hashtable

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值