Java集合面试题(持续更新)
概念
数组和集合的区别,你用过哪些
- 数组是固定的,一旦创建便不可改变。而集合是可变的,可以动态调整元素个数。
- 数组可以存储基本数据类型和对象类型,但是集合只能存储对象。
- 数组可以使用下标直接访问元素,而集合需要迭代器或者其他方法访问元素
我用过如下Java集合类:
- ArrayList:动态数组,实现了List接口,支持动态增长
- LinkList:双线链表,也实现了List接口,支持快速插入和删除
- HashMap:基于hash表的map实现,存储key-value,通过key快速查找
- HashSet:基于hash实现的set集合,存储唯一元素
- TreeMap:基于红黑树实现的有序map集合,可以按照key的顺序排序
- LinkHashMap:基于hash和双向链表实现的map集合,保持插入顺序和访问速度
- ProorityQueue:优先队列,按照比较器或元素的自然顺序进行排序
说说Java中的集合
List
List是有序的Collection,使用这个接口可以精确的控制每个元素的位置,用户可以根据索引访问list中的元素。
- ArrayList:是容量可变的非线程安全列表,其底层使用的是数组实现,当扩容时,会扩大创建更大的数组,然后将原数组复制到新数组。ArrayList可以对元素快速访问,但是随机插入删除很慢。
- LinkList:本质是一个双向链表,与ArrayList相比,随机查找比较满,但是随机插入和删除比较快
Set
set不允许存在重复的元素,与list不同,set是无序的。
HashSet:是通过HashMap实现的,HashMap的key就是HashSet存储元素,其中的value值都是一样的,使set内元素保持唯一性。由于hashset由hashmap实现的,因此线程不安全。
LinkHashSet:继承自HashSet,使用双向链表保持元素插入的顺序,
TreeSet:通过TreeMap实现,添加数据按照比较规则将其插入合适的位置,保证插入数据的有序性。
Map
是一个键值对集合,存储键值之间的映射。key无序,要求唯一;value不要求有序,允许重复。map没有继承Collection,从map中获取元素时,只要给出键对象,就会返回相应的value。
HashMap:在Java8之前,hashmap采用数组+链表去实现的,数组是hashmap的主体,链表是为了解决hash冲突存储的(拉链法)。【ps:在Java8之前,当一个key要插入hashmap时,需要根据相应的hash公式计算出key该放在数组的哪个位置,但是数组长度有限,很可能出现撞车现象,那么此时我们在数组内存放一个链表,链表内存储键值对,然后将撞车的key插入到头部。这就是拉链法】。但是在Java8之后发生了改变,当链表长度大于8时,会将链表转化为红黑树。
LinkHashMap:继承自hashmap,底层还是基于链表+数组+红黑树数组。但是在上面结构的情况下,添加了一条双向链表,可以保持插入的顺序。
HashTable:数组+链表组成。
TreeMap:红黑树
ConcurrentHashMap:Node数组+链表+红黑树实现,是线程安全的(Java8之前是Segment锁,1.8之后采用synchronized实现)
Java中的线程安全集合是什么
java.util
vector:线程安全的动态数组,其内部方法基本都是经过synchronize修饰,但是如果不需要线程安全,不建议使用,同步是有额外开销的
HashTable:线程安全的hash表,给每个方法加上synchronized关键字,这样锁住的就是整个table对象。
java.util.concurrent
并发Map:
- ConcurrentHashMap:他与HashTable的主要区别是二者加锁力度不同,在Java8之前,ConcurrentHashMap加的是分段锁,也就是Segment,每个Segment都含有table的一部分,这样不同分段之间的并发就不受影响。在Java8之后,他取消了Segment字段,直接在table元素上加锁,实现对每一行进行枷锁,进一步减少了并发冲突的概率。对于put操作,如果key对应的数组为null,则通过CAS操作将其设置为当前值,如果key对应的数组元素不为null,则对该元素使用sychronized关键字申请加锁,如果该链表的长度大于8,则将链表转化为红黑树。
- ConcurrentSkipListMap:基于跳表算法的可排序的并发集合。
并发set:
- ConcurrentSkipListSet:是线程安全的有序集合,底层是基于ConcurrentSkipListMap实现的
- ConcurrentOnWriteAtrraySet:是线程安全的set实现,他是线程安全的无序集合。
并发list:
- CopyOnWriteArrayList:他是ArrayList线程安全的变体,其中所有写操作都通过对底层数组进行全新复制来实现,允许存储null。当对象进行写操作时,使用lock进行同步处理,内部拷贝了原数组,并在新的数组上进行添加操作,最后将新数组覆盖掉旧数组。
Collections和Collention的区别
- Collection时Java集合类的一个基础接口,他定义了一系列的通用操作和方法,如添加、删除、遍历等,用户同意操作和管理。List、Set都是他的实现类
- Collentions是Java提供的一个工具类,位于java.util中,他提供了一系列的静态方法,用于对集合进行操作和算法,比如排序、查找、替换等。这些方法可以对实现了Collent的接口进行操作
遍历集合的方法
普通for循环
List<String> list = new ArrayList<>();
for(int i=0;i<list.size();i++){
}
增强for循环 也即是for-each
//这里不再定义集合,使用list代表集合
for(String element:list){
//业务操作
}
Iterator迭代器
用来迭代遍历集合,用户要删除元素的状况
//这里不再定义集合了 使用list代替
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String ele = iterstor.next();
//业务操作
}
使用Java8后的Stream API
//这里不再定义list
list.forEach(element->log.info(element))