Java集合相关面试题

常用的集合类有哪些?

Map接口和Collection接口是所有集合框架的父接口

  1. Collection接口的子接口包括:Set接口和List接口
  2. Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
  3. Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
  4. List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector

Collection和Collections的区别,介绍一下Collectionn的结构?

Java.util.Collection是一个集合接口,Collection接口在Java类库中有非常多的实现。
Java.util.Collections是针对集合类提供的一个帮助类,它提供了一系列的静态方法实现
对集合的搜索、排序、线程安全化的操作。
在这里插入图片描述

ArrayList和LinkedList的区别是什么?

数据结构:ArrayList底层是动态数组,而LinkedList是双向链表的数据结构
随机访问:由于ArrayList底层是数组,通过下标获取数据所以效率较高
增加删除:在非首尾的增加和删除操作,LinkedList要比ArrayList效率高
因为ArrayList增加删除要影响其它元素的下标

什么是双向链表?双向链表和单链表的区别?

链表是指内存的底层元素和元素不是连续存储的,只是靠着引用指向来形成先后关系

ArrayList和LinkedList哪个需要扩容?扩容机制是怎么样的?

ArrayList才需要扩容

ArrayList() 创建对象时,不会定义底层数组的长度,当第一次调用add(E e) 方法时,初始化定义底层数组的长度为10,之后调用add(E e)时,如果需要扩容,则调用grow(int minCapacity) 进行扩容,长度为原来的1.5倍。

List和Set的区别?

Set 集合要求元素必须唯一 对顺序没有要求 SortedSet 要求唯一且有序
List 集合有序而且不唯一 有序:默认按照添加的顺序 可以指定排序
Collections.sort(list 集合,比较器)

说一下 HashSet 的实现原理?

HashSet 是基于 HashMap 实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为PRESENT,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层HashMap 的相关方法来完成,HashSet 不允许重复的值。

如何遍历list并且删除元素?

可以使用迭代器的 remove()方法
否则在遍历list的过程中直接对list集合进行删除操作会触发CME并发修改异常

为了防止并发错误的出现 util包当中所有的集合都会有一套比较完整的判断机制
这套判断机制具体指集合本身就总共操作次数
当我们得到迭代器的时候 迭代器会同步记录这个操作次数
然而当我们直接操作集合进行添加或者删除后,迭代器本身更新这个操作
次数从而在下次调用next()方法的时候导致校验并不成功(迭代器会认为有其它线程跟它一起操作数据)可能导致并发错误。

多线程场景下如何使用 ArrayList?

ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用

List<String> synchronizedList = Collections.synchronizedList(list);
synchronizedList.add("aaa");
synchronizedList.add("bbb");
for (int i = 0; i < synchronizedList.size(); i++) {
  System.out.println(synchronizedList.get(i));
}

HashMap底层的数据结构?

在JDK 1.7的时候HashMap底层是一个数组+链表的数据结构,JDK 1.8中底层改为了数组+链表+红黑树,加红黑树的目的是为了提高整体的查询效率。
1.7中链表插入采用的是头插法,1.8中链表使用的是尾插法,因为1.8中插入key和value的时候需要判断元素个数,所以需要遍历链表统计元素个数。
1.7中hash算法比较复杂,存在各种右移与异或运算,1.8中进行了简化,因为复杂的哈希
算法的目的就是提高散列性,来提供HashMap的整体效率,而1.8中新增了红黑树,所以可以适当简化哈希算法,节省CPU资源。

说一下HashMap的put方法?

1.根据Key通过hash运算得出数组下标
2.如果数组下标位置元素为空,则将key和value封装为一个Entry对象(JDK 1.7中是一个Entry JDK 1.8中是一个Node对象)并放入该位置
3.如果数组下标元素不为空
(1)如果是JDK 1.7,则先判断是否需要扩容,如果需要扩容就进行扩容,如果不需要扩容就生成Entry对象,并且使用头插法添加到当前位置的链表中
(2)如果是 JDK 1.8,则会先判断当前位置的Node的类型,看是红黑树Node,还是链表Node
1)如果是红黑树Node,则将key和value封装为一个红黑树节点并添加到红黑树中,在这个过程中会先判断红黑树中是否存在key,如果存在则更新value
2) 如果是链表,则将key和value封装为一个Node并通过尾插法插入到链表的最后,因为是尾插法,所以需要遍历链表,在遍历链表的过程中会判断是否存在当前key,如果存在则
更新,当遍历完链表后,将新链表Node插入链表中,插入到链表后,会看当前链表的节点个数,如果大于等于8,那么将会将链表转化为红黑树。
3)将key和value封装为Node插入到链表或红黑树后,再判断是否需要扩容,如果需要就扩容,如果不需要就结束Put方法。

HashMap的扩容机制原理?

JDK 1.7
1.先生成新数组
2.遍历老数组中每个位置上的链表上的每个元素
3.取出每个元素的key,并基于新数组长度,计算出每个元素在新数组中的下标
4.将元素添加到新数组中
5.所有元素转移完成之后,将新数组赋值给HashMap对象的table属性

JDK 1.8
1.先生成新数组
2.遍历老数组中的每个位置上的链表或红黑树
3.如果是链表,则直接将链表中每个元素重新计算下标,并添加到新数组中
4.如果是红黑树,则先遍历红黑树,先计算出红黑树中每个元素对应在新数组
的下标位置。
(1)统计每个下标位置的元素个数
(2)如果该位置下的元素超过了8 ,则生成一个新的红黑树,并将根节点添加到
新数组的对应位置。
(3)如果该位置下的元素个数没有超过8,那么则生产厂一个链表,并将链表的头节点
添加到新数组的对应位置。
5.所有元素转移完成之后,将新数组赋值给HashMap对象的table属性

HashMap和HashTable的区别?

  1. HashTable 底层大量的方法使用了Synchronized()所以线程安全
  2. HashMap 无论主键对象 还是值对象 都允许添加null Hashtable 无论主键还是值 都不允许出现null
  3. HashMap 默认分为16个小组 分组组数可以随意指定 但是最终结果一定是2的n次方数 HashMap : hash & (分组组数-1)Hashtable 默认分为11个小组 分组组数可以随意指定 Hashtable : hash % 分组组数
  4. HashMap since JDK1.2 Hashtable JDK1.0

HashMap是有序的吗?如何实现有序?

HashMap是无序的
使用TreeMap

CopyOnWriteArrayList的底层原理是怎样的

  1. ⾸先CopyOnWriteArrayList内部也是⽤过数组来实现的,在向CopyOnWriteArrayList添加元素
    时,会复制⼀个新的数组,写操作在新数组上进⾏,读操作在原数组上进⾏
  2. 并且,写操作会加锁,防⽌出现并发写⼊丢失数据的问题
  3. 写操作结束之后会把原数组指向新数组
  4. CopyOnWriteArrayList允许在写操作时来读取数据,⼤⼤提⾼了读的性能,因此适合读多写少的应
    ⽤场景,但是CopyOnWriteArrayList会⽐较占内存,同时可能读到的数据不是实时最新的数据,所以不适合实时性要求很⾼的场景

BlockingQueue是什么?

Java.util.concurrent.BlockingQueue是一个队列,在进行检索或移除一个元素的时候,它会等待队列变为非空;当在添加一个元素时,它会等待队列中的可用空间。BlockingQueue接口是Java集合框架的一部分,主要用于实现生产者-消费者模式。我们不需要担心等待生产者有可用的空间,或消费者有可用的对象,因为它都在BlockingQueue的实现类中被处理了。Java提供了集中BlockingQueue的实现,比如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。

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

Array 转 List: Arrays. asList(array) ;
List 转 Array:List 的 toArray() 方法。

comparable 和 comparator的区别?

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

TreeMap 和 TreeSet 在排序时如何比较元素?Collections 工具类中的 sort()方法如何比较元素?

TreeSet 要求存放的对象所属的类必须实现 Comparable 接口,该接口提供了比较元素的 compareTo()
方法,当插入元素时会回调该方法比较元素的大小。TreeMap 要求存放的键值对映射的键必须实现
Comparable 接口从而根据键对元素进 行排 序。
Collections 工具类的 sort 方法有两种重载的形式,
第一种要求传入的待排序容器中存放的对象比较实现 Comparable 接口以实现元素的比较;
第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator 接口
的子类型(需要重写 compare 方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通
过接口注入比较元素大小的算法,也是对回调模式的应用(Java 中对函数式编程的支持)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值