java面试题-⭐集合框架篇⭐(万字总结,带答案,面试官问烂,跳槽必备,建议收藏)

👀个人主页: Java李小立
后面会持续更新java面试专栏,请持续关注
如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连❤️❤️❤️)

面试宝典列表(持续更新):

序号内容链接地址
1Java基础篇(点击跳转)java面试宝典-基础篇
2Java集合框架篇(点击跳转)java面试宝典-集合框架篇
3Java多线程篇(点击跳转)java面试宝典- 多线程篇
4JVM篇待分享
5Spring篇待分享
6Mybatis篇待分享
7SpringcCloud篇待分享
8Redis篇待分享
9Mysql篇待分享
10dubbo篇待分享
11zookeeper篇待分享
12kafka篇待分享
13RocketMq篇待分享
14Nacos篇待分享

(一)集合框架知识点

首先附两张图展现集合框架整体

collection:
在这里插入图片描述
Map:
在这里插入图片描述

1.1. List,Set,Map三者的区别及总结

  1. List:对付顺序的好帮手

List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象

  1. Set:注重独一无二的性质

不允许重复的集合。不会有多个元素引用相同的对象。

  1. Map:用Key来搜索的专家

使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。

1.2. Arraylist 与 LinkedList 区别

Arraylist底层使用的是数组(存读数据效率高,插入删除特定位置效率低)。
LinkedList底层使用的是双向循环链表数据结构(插入,删除效率特别高)。
学过数据结构这门课后我们就知道采用链表存储,插入,删除元素时间复杂度不受元素位置的影响,都是近似O(1)而数组为近似O(n),因此当数据特别多,而且经常需要插入删除元素时建议选用LinkedList.一般程序只用Arraylist就够用了,因为一般数据量都不会蛮大,Arraylist是使用最多的集合类。

1.3.ArrayList 与 Vector 区别

Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector ,代码要在同步操作上耗费大量的时间。Arraylist不是同步的,所以在不需要同步时建议使用Arraylist。

1.4. HashMap 和 Hashtable 的区别

  1. HashMap是非线程安全的,HashTable是线程安全的;HashTable内部的方法基本都经过synchronized修饰。
  2. 因为线程安全的问题,HashMap要比HashTable效率高一点,HashTable基本被淘汰。
  3. HashMap允许有null值的存在,而在HashTable中put进的键值只要有一个null,直接抛出NullPointerException。

1.5. HashSet 和 HashMap 区别

在这里插入图片描述

1.6.HashMap 和 ConcurrentHashMap 的区别

  1. ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的synchronized锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。(JDK1.8之后ConcurrentHashMap启用了一种全新的方式实现,利用CAS算法。)
  2. HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。

1.7. HashSet如何检查重复

当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。

1.8. comparable 和 comparator的区别

  1. comparable接口实际上是出自java.lang包 它有一个 compareTo(Object obj)方法用来排序
  2. comparator接口实际上是出自 java.util 包它有一个compare(Object obj1, Object obj2)方法用来排序
    一般我们需要对一个集合使用自定义排序时,我们就要重写compareTo方法或compare方法,当我们需要对某一个集合实现两种排序方式,比如一个song对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写compareTo方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的Collections.sort().

1.9. Collection包结构,与Collections的区别

Collection是集合类的上级接口,子接口有 Set、List、LinkedList、ArrayList、Vector、Stack、
Set;
Collections是集合类的一个帮助类, 它包含有各种有关集合操作的静态多态方法,用于实现对各种
集合的搜索、排序、线程安全化等操作。此类不能实例化,就像一个工具类,服务于Java的
Collection框架。

1.10. 如何对Object的list排序

1.对objects数组进行排序,我们可以用Arrays.sort()方法
2.对objects的集合进行排序,需要使用Collections.sort()方法

1.11. 如何实现数组与List的相互转换

List转数组: list.toArray(arraylist.size())方法;
数组转List: Arrays的asList(arr)方法

1.12. 集合框架底层数据结构总结

Collection

  1. List
    Arraylist:数组(查询快,增删慢 线程不安全,效率高 )
    Vector:数组(查询快,增删慢 线程安全,效率低 )
    LinkedList:链表(查询慢,增删快 线程不安全,效率高 )
  2. Set
    HashSet(无序,唯一):哈希表或者叫散列集(hash table)
    LinkedHashSet:链表和哈希表组成 。 由链表保证元素的排序 , 由哈希表证元素的唯一性
    TreeSet(有序,唯一):红黑树(自平衡的排序二叉树。)

Map
3. HashMap:基于哈希表的Map接口实现(哈希表对键进行散列,Map结构即映射表存放键值对)
4. LinkedHashMap:HashMap 的基础上加上了链表数据结构
5. HashTable:哈希表
6. TreeMap:红黑树(自平衡的排序二叉树)

(二)集合框架面试题

2.1. 详细谈一下HashMap

这个能说多少说多少,很大概率必问,推荐百度一下

java7的实现

在这里插入图片描述
HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。上图中,每个蓝色的实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。

  1. capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。
  2. loadFactor:负载因子,默认为 0.75。
  3. threshold:扩容的阈值,等于 capacity * loadFactor

java8的实现

在这里插入图片描述

Java8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树组成。

根据 Java7 HashMap 的介绍,我们知道,查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为 O(n)。为了降低这部分的开销,在 Java8 中,当链表中的元素超过了 8 个以后,会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。

2.2. HashMap 中的 key 我们可以使用任何类作为 key 吗?

平时可能大家使用的最多的就是使用 String 作为 HashMap 的 key,但是现在我们想使用某个自定义类作为 HashMap 的 key,那就需要注意以下几点:

  • 如果类重写了 equals 方法,它也应该重写 hashCode 方法。
  • 类的所有实例需要遵循与 equals 和 hashCode 相关的规则。
  • 如果一个类没有使用 equals,你不应该在 hashCode 中使用它。
  • 咱们自定义 key 类的最佳实践是使之为不可变的,这样,hashCode 值可以被缓存起来,拥有更好的性能。不可变的类也可以确保 hashCode 和 equals 在未来不会改变,这样就会解决与可变相关的问题了。

2.3. HashMap 的长度为什么是 2 的 N 次方呢?

为了能让 HashMap 存数据和取数据的效率高,尽可能地减少 hash 值的碰撞,也就是说尽量把数据能均匀的分配,每个链表或者红黑树长度尽量相等。
我们首先可能会想到 % 取模的操作来实现。

取余(%)操作中如果除数是 2 的幂次,则等价于与其除数减一的与(&)操作(也就是说hash % length == hash &(length - 1) 的前提是 length 是 2 的 n 次方)。并且,采用二进制位操作 & ,相对于 % 能够提高运算效率

这就是为什么 HashMap 的长度需要 2 的 N 次方了。

2.4. 说说Hashtable 与 HashMap 的区别

虽然Hashtable很少使用,但面试官还是喜欢这样问,无奈小立给大家还是总结一下。

  1. 出生的版本不一样,Hashtable 出生于 Java 发布的第一版本 JDK 1.0,HashMap 出生于 JDK1.2。
  2. 都实现了 Map、Cloneable、Serializable(当前 JDK 版本 1.8)。
  3. HashMap 继承的是 AbstractMap,并且 AbstractMap 也实现了 Map 接口。Hashtable 继承Dictionary。
  4. Hashtable 中大部分 public 修饰普通方法都是 synchronized 字段修饰的,是线程安全的,HashMap 是非线程安全的。
  5. Hashtable 的 key 不能为 null,value 也不能为 null,这个可以从 Hashtable 源码中的 put 方法看到,判断如果 value 为 null 就直接抛出空指针异常,在 put 方法中计算 key 的 hash 值之前并没有判断 key 为 null 的情况,那说明,这时候如果 key 为空,照样会抛出空指针异常。
  6. HashMap 的 key 和 value 都可以为 null。在计算 hash 值的时候,有判断,如果key==null ,则其 hash=0 ;至于 value 是否为 null,根本没有判断过。
  7. Hashtable 直接使用对象的 hash 值。hash 值是 JDK 根据对象的地址或者字符串或者数字算出来的 int 类型的数值。然后再使用除留余数法来获得最终的位置。然而除法运算是非常耗费时间的,效率很低。HashMap 为了提高计算效率,将哈希表的大小固定为了 2 的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。
  8. Hashtable、HashMap 都使用了 Iterator。而由于历史原因,Hashtable 还使用了Enumeration 的方式。
  9. 默认情况下,初始容量不同,Hashtable 的初始长度是 11,之后每次扩充容量变为之前的2n+1(n 为上一次的长度)而 HashMap 的初始长度为 16,之后每次扩充变为原来的两倍。

这个回答完了,面试官可能会继续问:HashMap 是线程不安全的,那么在需要线程安全的情况下
还要考虑性能,有什么解决方式?
这里最好的选择就是 ConcurrentHashMap 了,但面试官肯定会叫你继续说一下ConcurrentHashMap 数据结构以及底层原理等。

2.5. 说一下ConcurrentHashMap

Segment 段
ConcurrentHashMap 和 HashMap 思路是差不多的,但是因为它支持并发操作,所以要复杂一些。整个 ConcurrentHashMap 由一个个 Segment 组成,Segment 代表”部分“或”一段“的意思,所以很多地方都会将其描述为分段锁。
线程安全
Segment 继承 ReentrantLock 加锁 简单理解就是ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。

java7的实现

在这里插入图片描述
并行度(默认 16)
concurrencyLevel:并行级别、并发数、Segment 数,怎么翻译不重要,理解它。默认是 16,也就是说 ConcurrentHashMap 有 16 个 Segments,所以理论上,这个时候,最多可以同时支持 16 个线程并发写,只要它们的操作分别分布在不同的 Segment 上。这个值可以在初始化的时候设置为其他值,但是一旦初始化以后,它是不可以扩容的。再具体到每个 Segment 内部,其实每个 Segment 很像之前介绍的 HashMap,不过它要保证线程安全,所以处理起来要麻烦些。

java8的实现

在这里插入图片描述
采用 Node + CAS + Synchronized来保证并发安全进行实现。

2.6. HashMap 与 ConcurrentHashMap 的异同

  1. 都是 key-value 形式的存储数据;
  2. HashMap 是线程不安全的,ConcurrentHashMap 是 JUC 下的线程安全的;
  3. HashMap 底层数据结构是数组 + 链表(JDK 1.8 之前)。JDK 1.8 之后是数组 + 链表 + 红黑树。当链表中元素个数达到 8 的时候,链表的查询速度不如红黑树快,链表会转为红黑树,红黑树查询速度快;
  4. HashMap 初始数组大小为 16(默认),当出现扩容的时候,以 0.75 * 数组大小的方式进行扩容;
  5. ConcurrentHashMap 在 JDK 1.8 之前是采用分段锁来现实的 Segment + HashEntry,Segment 数组大小默认是 16,2 的 n 次方;JDK 1.8 之后,采用 Node + CAS + Synchronized来保证并发安全进行实现。

2.7. 红黑树有哪几个特征?

面试官问道HashMap很大概率又会继续问红黑树
在这里插入图片描述

2.8. 说说List,Set,Map三者的区别?

  • List(对付顺序的好帮手): List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
  • Set(注重独一无二的性质): 不允许重复的集合。不会有多个元素引用相同的对象。
  • Map(用Key来搜索的专家):使用键值对存储。 Map会维护与Key有关联的值。两个Key可以引 用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。

2.9. ArrayList 和 LinkedList 的区别有哪些?

ArrayList

  • 优点:ArrayList 是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询 操作效率会比较高(在内存里是连着放的)。
  • 缺点:因为地址连续,ArrayList 要移动数据,所以插入和删除操作效率比较低。

LinkedList

  • 优点:LinkedList 基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址。对于新增和删除操作,LinkedList 比较占优势。LinkedList 适用于要头尾操 作或插入指定位置的场景。
  • 缺点:因为 LinkedList 要移动指针,所以查询操作性能比较低。

适用场景分析

  • 当需要对数据进行对随机访问的时候,选用 ArrayList。
  • 当需要对数据进行多次增加删除修改时,采用 LinkedList。

如果容量固定,并且只会添加到尾部,不会引起扩容,优先采用 ArrayList。

当然,绝大数业务的场景下,使用 ArrayList 就够了,但需要注意避免 ArrayList 的扩容,以及非顺序的插入

2.10. 有数组了为什么还要搞个 ArrayList 呢?

通常我们在使用的时候,如果在不明确要插入多少数据的情况下,普通数组就很尴尬了,因为你不知道需要初始化数组大小为多少,而 ArrayList 可以使用默认的大小,当元素个数到达一定程度后,会自动扩容。可以这么来理解:我们常说的数组是定死的数组,ArrayList 却是动态数组。

2.11. 用过 ArrayList 吗?说一下它有什么特点?

ArrayList是Java 集合框架中的一种存放相同类型的元素数据,是一种变长的集合类,基于定长数组实现,当加入数据达到一定程度后,会实行自动扩容,即扩大数组大小。
底层是使用数组实现,添加元素。

  • 如果 add(o),添加到的是数组的尾部,如果要增加的数据量很大,应该使用 ensureCapacity()方法,该方法的作用是预先设置 ArrayList 的大小,这样可以大大提高初始化速度。
  • 如果使用 add(int,o),添加到某个位置,那么可能会挪动大量的数组元素,并且可能会触发扩容机制。
    高并发的情况下,线程不安全。多个线程同时操作 ArrayList,会引发不可预知的异常或错误。
    ArrayList 实现了 Cloneable 接口,标识着它可以被复制。注意:ArrayList 里面的 clone() 复制其实是浅复制。

版权声明:本文为博主原创文章,未经博主允许不得转载
https://blog.csdn.net/qq_44614710/article/details/113694683

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值