目录
目录
介绍Java的List,ArrayList与LinkedList的区别
Array(数组)和ArrayList有何区别?什么时候更适合用Array?
通过Array.asList获得的List有何特点,使用时应该注意什么?
ArrayList的删除,需要注意List删除后下标变化的坑(用Iterator的写法)
什么是fail-fast,什么是fail-safe,有什么区别吗?
在迭代一个集合的时候,如何避免ConcurrentModificationException?
HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标?
为什么HashMap中String、Integer这样的包装类适合作为Key?我们能否使用任何类作为Map的key?如果能需要注意哪些问题
ConcurrentHashMap和Hashtable的区别?
ConcurrentHashMap在JDK1.7和JDK1.8中有哪些不同?
集合在遍历过程中是否可以删除元素,为什么迭代器就可以安全删除元素
一、集合框架概述
Java集合框架是一个很重要的内容,面试中也是属于必考的部分,那么首先,什么是集合?什么是集合框架呢?集合框架有什么优点?
通常我们把具有相同性质的一类东西,汇聚成一个整体,就可以称为集合。集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算的算法。
集合框架的优点:
(1)使用核心集合类降低开发成本,而非实现我们自己的集合类。
(2)随着使用经过严格测试的集合框架类,代码质量会得到提高。
(3)通过使用JDK附带的集合类,可以降低代码维护成本。
(4)复用性和可操作性。
集合框架常用实现类如下:
在集合框架中,主要可以分为三个大类:list(列表)、set(集)、map(映射)
list是有序的,元素按照添加进list的顺序排序,元素可以重复,访问时可以根据元素的索引来访问,使用Iterator时的遍历顺序和元素添加的顺序一致;
set是无序的,因此使用迭代器(Iterator)的时候,不能保证元素的遍历顺序,但是可以保证所有元素被遍历;此外,set中不允许出现重复的元素,访问的时候只能通过元素本身来访问(这也是元素不能重复的原因);
map保存的是<key,value>键值对,key不能重复,但是value可以重复,访问时只能根据key来访问value,使用Iterator时,对key进行遍历,然后根据每个key访问相应的value;
此外,还有queue接口和一些在并发中涉及到的集合。
二、面试重点
1、List接口
List接口是Collection接口的子接口,通常用于表示有序的数组(可以动态扩容),链表,队列,栈等。其实现类主要有3个:ArrayList、LinkedList 和 Vector(线程安全)。
面试中主要涉及的问题有:
介绍Java的List,ArrayList与LinkedList的区别
ArrayList与LinkedList、Vector的区别
Array(数组)和ArrayList有何区别?什么时候更适合用Array?
区别:
- 数组的长度固定不变,而ArrayList是动态数组,在容量不够是会进行1.5倍的扩容
- 数组的元素可以是基本类型或者对象,但ArrayList只能是对象(直接放入的基本类型经过自动装箱成为包装类对象)
- 数组中所有元素类型必须是一样的,但是ArrayList可以存储Object对象,因此如果把泛型类型限制为Object则可以存储不同类型的元素
- ArrayList比数组的方法更加丰富,还可以有自己的迭代器对象
使用场景:
- 当集合长度固定,并且只需要对数组进行简单的随机读写,使用数组,因为数组占用内存更低;如果集合长度可变,并且需要有插入、删除等操作,选择ArrayList,因为ArrayList集成了相关操作,并且可以自动扩容。当然如果插入和删除的操作十分的频繁还是使用LinkedList。
- 当存储的时基本数据类型,因为ArrayList不支持基本数据类型,需要进行自动装箱和拆箱,效率相对下降,因此尽量选择数组。
List是线程安全的吗?如果要线程安全要怎么做?
实现list实现类的线程安全:
- List接口下的实现类,只有Vector及其子类Stack时线程安全的,其他都是非线程安全的,因此可以使用Vector来代替ArrayList
- 可以使用Collections.synchronizedList(List<T> list)方法包装list的实现类即可,同样的可以用Collections.synchronizedSet(Set<T> set)、Collections.synchronizedMap(Map<K,V> m)、Collections.synchronizedCollection(Collection<Object>)方法包装集合框架的其他非线程安全实现类来实现线程安全的类。
两种方法的区别:
-
如果使用add方法,那么他们的扩容机制不一样(类似arraylist和Vector的区别)。
-
SynchronizedList可以通过Collections.synchronizedList(List<T> list,Object mutex)指定锁定的对象。通过Collections的源代码也可以看出Collections.synchronizedList返回的是内部类SynchronizedList,其中同步的实现是通过synchronized(mutex)同步代码块实现的,可以认为Collections.synchronizedList锁粒度是同步代码块。而Vector的锁粒度是同步方法。
-
SynchronizedList有很好的扩展性和兼容功能。他可以将所有的list子类转成线程安全的类。
-
使用SynchronizedList的时候,进行遍历时需要手动进行同步处理,这是因为Collections.synchronizedList的迭代器iterator并没有做同步处理,而是直接使用了list的iterator。
怎么给List排序?
通过Array.asList获得的List有何特点,使用时应该注意什么?
asList()方法返回的list的特点:
Array.asList()方法的作用是将数组转换为list集合,但是通过其源代码可以发现,Array.asList()返回的是Arrays的一个内部类,该内部类只是实现了AbstractList接口,但是其后台仍然是数组的数据结构,也就是说,得到的只是原来数组的视图List,而没有实现list集合的add和remove等修改方法,因此如果对他进行增删操作会报UnsupportOperationException异常。
具体案例参考:https://www.cnblogs.com/hoobey/p/6661294.html
如何实现得到真的list:
用ArrayList的构造器可以将其转变成为一个真正的ArrayList
Integer[] a={1,2,3,4,5};
ArrayList al= new ArrayList(Arrays.asList(a));
需要注意的是,Array.asList()方法对基本数据类型的数组是无效的:如下代码所示,对于基本数据类型,ArrayList中的元素只有一个,就是数组本身。
int[] a={1,2,3,4,5};
ArrayList al= new ArrayList(Arrays.asList(a));
System.out.println(al.size());
//返回:1
List和Array之间如何互相转换?
- 数组(Array)转list:Array.asList()方法,同上
- list转数组(Array):list.toArray()方法
ArrayList的删除,需要注意List删除后下标变化的坑(用Iterator的写法)
什么是fail-fast,什么是fail-safe,有什么区别吗?
fail-fast 与 fail-safe 机制的原理与区别
List的迭代方式有哪些?
Collection集合实现类(list、set和queue)的集中迭代方式
在迭代一个集合的时候,如何避免ConcurrentModificationException?
什么时候会出现ConcurrentModificationException异常:
首先,ConcurrentModificationException异常是当条件modCount != expectedModCount成立时产生的,而这个比较是在迭代器(Iterator)的checkForComodification()方法中出现的,而Iterator的next()和remove()都调用了该方法,而expectedModCount是在获取迭代器的时候初始化的,也就是说,如果在调用Iterator的next()和remove()时modCount被修改,那么checkForComodification()方法会抛出ConcurrentModificationException异常。那么,什么情况会导致modCount被修改呢?add()、remove()操作都会。(foreach循环底层也是使用迭代器因此也hi出现该异常)
因此,在使用迭代器(Iterator)的时候使用list.add()、list.clear()和list.remove()方法都会导致ConcurrentModificationException异常。
详细代码参考:https://www.jianshu.com/p/00be866fcb18
如何避免ConcurrentModificationException异常:
- 如果是remove()操作,使用迭代器(Iterator)自己的reemove()方法;listIterator有add()和remove()方法;
- 使用Collections.synchronizedCollection() 去同步集合, 但是这样可能会影响效率,;
- 使用concurrent包里的CopyOnWriteArrayList, 因为他的迭代器中没有checkForComodification;
2、set接口
set也是Collection接口的子接口,常见实现类有HashSet和TreeSet 。set集合中不允许有重复的元素是因为set集合用来比较两个元素是否相等时使用的是equals()方法,而不是“==”运算符,因此只要两个元素内容相同,就不能在一个set中共存,如果已经在set集合中存在了a元素,再使用add()方法添加a元素会返回false。
面试中主要涉及的问题有:
HashSet是如何保证数据不可重复的?
HashSet 底层数据结构
LinkedHashSet 底层数据结构及其特点
TreeSet的底层数据结构及其特点
3、map接口
map存储的是<key,value>键值对,而每个值又可以是一个map,以此类推,可以实现多层的映射。map接口常见的实现类有HashMap,TreeMap,HashTable等。map的key和set一样,不允许重复,但是value可以重复。从概念上来看,可以把List看做一种特殊的map,把List的索引当做key,把list的元素当做value,这样就可以形成键值对,但是事实上List和map并没有太多的关系。
面试中主要涉及的问题有:
HashMap的底层数据结构
HashMap与HashTable的区别?
HashMap是否线程安全,体现在什么地方?
HashMap的扩容操作是怎么实现的?
为什么数组长度要保持为2的幂次方?
HashMap是怎么解决哈希冲突的?
HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标?
HashMap在JDK1.7和JDK1.8中有哪些不同?
为什么HashMap中String、Integer这样的包装类适合作为Key?我们能否使用任何类作为Map的key?如果能需要注意哪些问题
HashMap和HashTable有何不同?
ConcurrentHashMap和Hashtable的区别?
ConcurrentHashMap在JDK1.7和JDK1.8中有哪些不同?
4、Queue接口
BlockingQueue的特点?
队列和栈的区别?