锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

JAVA集合面试题52道

时间:2022-09-21 00:00:00 连接器fh52

集容器概述
1. 什么是集合
集合是一个放置数据的容器准确地说是放数据对象引用的容器
集合存储是对象的引用,而不是对象本身
主要有集合类型 3 种: set( 集)、 list( 列表)和 map( 映射 )
2. 集合的特点
集合的特点主要有以下两点:
集合用于存储对象的容器用于包装数据,对象较多也需要存储集中管理。
与数组对比对象的大小不确定。因为集合是可变长度。数组需要提前定义大小
3. 集合和数组的区别
固定长度的数组;集合可变长度。
数组可以存储基本数据类型或引用数据类型;集合只能存储引用数据类型。
数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。
4. 使用集合框架的好处
1. 容量自增长;
2. 提供高性能的数据结构和算法,使编码更容易,提高程序速度和质量;
3. 可以方便地扩展或重写集合,提高代码的再利用性和可操作性。
4. 通过使用 JDK 自带集合类可以减少代码维护和学习 API 成本。 5. 常用的集合类有哪些?
Map 接口和 Collection 接口是所有集合框架的父接口:
1. Collection 接口的子接口包括: Set 接口和 List 接口
2. Map 接口实现类主要包括: HashMap TreeMap Hashtable ConcurrentHashMap 以及
Properties
3. Set 接口实现类主要包括: HashSet TreeSet LinkedHashSet
4. List 接口实现类主要包括: ArrayList LinkedList Stack 以及 Vector
6. List Set span style="color:#333333;"> Map 三者的区别?
Java 容器分为 Collection Map 两大类, Collection 集合的子接口有 Set List Queue 三种子接
口。我们比较常用的是 Set List Map 接口不是 collection 的子接口。
Collection 集合主要有 List Set 两大接口
List :一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多
null 元素,元素都有索引。常用的实现类有 ArrayList LinkedList Vector
Set :一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一
null 元素,必须保证元素唯一性。 Set 接口常用实现类是 HashSet LinkedHashSet 以及
TreeSet
Map 是一个键值对集合,存储键、值和之间的映射。 Key 无序,唯一; value 不要求有序,允许重
复。 Map 没有继承于 Collection 接口,从 Map 集合中检索元素时,只要给出键对象,就会返回对应
的值对象。
Map 的常用实现类: HashMap TreeMap HashTable LinkedHashMap
ConcurrentHashMap 7. 集合框架底层数据结构
Collection
1. List
Arraylist Object 数组
Vector Object 数组
LinkedList : 双向循环链表
2. Set
HashSet (无序,唯一):基于 HashMap 实现的,底层采用 HashMap 来保存元素
LinkedHashSet LinkedHashSet 继承与 HashSet ,并且其内部是通过 LinkedHashMap
实现的。有点类似于我们之前说的 LinkedHashMap 其内部是基于 Hashmap 实现一样,不
过还是有一点点区别的。
TreeSet (有序,唯一): 红黑树 ( 自平衡的排序二叉树。 )
Map
HashMap JDK1.8 之前 HashMap 由数组 + 链表组成的,数组是 HashMap 的主体,链表则是
主要为了解决哈希冲突而存在的(
拉链法 解决冲突) .JDK1.8 以后在解决哈希冲突时有了较
大的变化,当链表长度大于阈值(默认为 8 )时,将链表转化为红黑树,以减少搜索时间
LinkedHashMap LinkedHashMap 继承自 HashMap ,所以它的底层仍然是基于拉链式散
列结构即由数组和链表或红黑树组成。另外, LinkedHashMap 在上面结构的基础上,增加
了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的
操作,实现了访问顺序相关逻辑。
HashTable : 数组 + 链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突
而存在的
TreeMap : 红黑树(自平衡的排序二叉树)
8. 哪些集合类是线程安全的?
Vector :就比 Arraylist 多了个 synchronized (线程安全),因为效率较低,现在已经不太建议使
用。
hashTable :就比 hashMap 多了个 synchronized ( 线程安全 ) ,不建议使用。
ConcurrentHashMap :是 Java5 中支持高并发、高吞吐量的线程安全 HashMap 实现。它由
Segment 数组结构和 HashEntry 数组结构组成。 Segment 数组在 ConcurrentHashMap 里扮演
角色, HashEntry 则用于存储键 - 值对数据。一个 ConcurrentHashMap 里包含一个 Segment 数组,
Segment 的结构和 HashMap 类似,是一种数组和链表结构;一个 Segment 里包含一个 HashEntry
数组,每个 HashEntry 是一个链表结构的元素;每个 Segment 守护着一个 HashEntry 数组里的元
素,当对 HashEntry 数组的数据进行修改时,必须首先获得它对应的 Segment 锁。(推荐使用)
...
9. Java 集合的快速失败机制 “fail-fast”
java 集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生
fail-fast 机制。
例如:假设存在两个线程(线程 1 、线程 2 ),线程 1 通过 Iterator 在遍历集合 A 中的元素,在某个时
候线程 2 修改了集合 A 的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这
个时候程序就会抛出 ConcurrentModifificationException 异常,从而产生 fail-fast 机制。 原因:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集
合在被遍历期间如果内容发生变化,就会改变 modCount 的值。每当迭代器使用 hashNext()/next()
遍历下一个元素之前,都会检测 modCount 变量是否为 expectedmodCount 值,是的话就返回遍
历;否则抛出异常,终止遍历。
解决办法:
1. 在遍历过程中,所有涉及到改变 modCount 值得地方全部加上 synchronized
2. 使用 CopyOnWriteArrayList 来替换 ArrayList
10. 怎么确保一个集合不能被修改?
可以使用 Collections. unmodififiableCollection(Collection c) 方法来创建一个只读集合,这样改变
集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。
示例代码如下:
Collection 接口
List 接口
11. 迭代器 Iterator 是什么?
Iterator 接口提供遍历任何 Collection 的接口。我们可以从一个 Collection 中使用迭代器方法来
获取迭代器实例。迭代器取代了 Java 集合框架中的 Enumeration ,迭代器允许调用者在迭代过程
中移除元素。
因为所有 Collection 接继承了 Iterator 迭代器
12. Iterator 怎么使用?有什么特点?
Iterator 使用代码如下:
List list = new ArrayList<>();
list. add("x");
Collection clist = Collections. unmodifiableCollection(list);
clist. add("y"); // 运行时此行报错
System. out. println(list. size());
List list = new ArrayList<>();
Iterator it = list. iterator();
while(it. hasNext()){
String obj = it. next();
System. out. println(obj);
} Iterator 的特点是只能单向遍历,但是更加安全,因为它可以确保,在当前遍历的集合元素被更改
的时候,就会抛出 ConcurrentModifificationException 异常。
13. 如何边遍历边移除 Collection 中的元素?
边遍历边修改 Collection 的唯一正确方式是使用 Iterator.remove() 方法,如下:
一种最常见的 错误 代码如下:
运行以上错误代码会报 ConcurrentModifificationException 异常 。这是因为当使用
foreach(for(Integer i : list)) 语句时,会自动生成一个 iterator 来遍历该 list ,但同时该 list 正在被
Iterator.remove() 修改。 Java 一般不允许一个线程在遍历 Collection 时另一个线程修改它。
14. Iterator ListIterator 有什么区别?
Iterator 可以遍历 Set List 集合,而 ListIterator 只能遍历 List
Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前 / 后遍历)。
ListIterator 实现 Iterator 接口,然后添加了一些额外的功能,比如添加一个元素、替换一个元
素、获取前面或后面元素的索引位置。
15. 遍历一个 List 有哪些不同的方式?每种方法的实现原理是什么? Java List
遍历的最佳实践是什么?
遍历方式有以下几种:
1. for 循环遍历,基于计数器。在集合外部维护一个计数器,然后依次读取每一个位置的元素,
当读取到最后一个元素后停止。
2. 迭代器遍历, Iterator Iterator 是面向对象的一个设计模式,目的是屏蔽不同数据集合的特
点,统一遍历集合的接口。 Java Collections 中支持了 Iterator 模式。
3. foreach 循环遍历。 foreach 内部也是采用了 Iterator 的方式实现,使用时不需要显式声明
Iterator 或计数器。优点是代码简洁,不易出错;缺点是只能做简单的遍历,不能在遍历过
程中操作数据集合,例如删除、替换。
最佳实践: Java Collections 框架中提供了一个 RandomAccess 接口,用来标记 List 实现是否支
Random Access
如果一个数据集合实现了该接口,就意味着它支持 Random Access ,按位置读取元素的平均
时间复杂度为 O(1) ,如 ArrayList
如果没有实现该接口,表示不支持 Random Access ,如 LinkedList
Iterator it = list.iterator();
while(it.hasNext()){
*// do something*
it.remove();
}
for(Integer i : list){
list.remove(i)
} 推荐的做法就是,支持 Random Access 的列表可用 for 循环遍历,否则建议用 Iterator
foreach 遍历。
16. 说一下 ArrayList 的优缺点
ArrayList 的优点如下:
ArrayList 底层以数组实现,是一种随机访问模式。 ArrayList 实现了 RandomAccess 接口,
因此查找的时候非常快。
ArrayList 在顺序添加一个元素的时候非常方便。
ArrayList 的缺点如下:
删除元素的时候,需要做一次元素复制操作。如果要复制的元素很多,那么就会比较耗费性
能。
插入元素的时候,也需要做一次元素复制操作,缺点同上。
ArrayList 比较适合顺序添加、随机访问的场景。
17. 如何实现数组和 List 之间的转换?
数组转 List :使用 Arrays. asList(array) 进行转换。
List 转数组:使用 List 自带的 toArray() 方法。
代码示例:
18. ArrayList LinkedList 的区别是什么?
数据结构实现: ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实
现。
随机访问效率: ArrayList LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数
据存储方式,所以需要移动指针从前往后依次查找。
增加和删除效率:在非首尾的增加和删除操作, LinkedList 要比 ArrayList 效率要高,因为
ArrayList 增删操作要影响数组内的其他数据的下标。
内存空间占用: LinkedList ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储
了两个引用,一个指向前一个元素,一个指向后一个元素。
线程安全: ArrayList LinkedList 都是不同步的,也就是不保证线程安全;
综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList ,而在插入和删除操作较多
时,更推荐使用 LinkedList
LinkedList 的双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向
直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结
点和后继结点。
// list to array
List list = new ArrayList();
list.add("123");
list.add("456");
list.toArray();
// array to list
String[] array = new String[]{"123","456"};
Arrays.asList(array); 19. ArrayList Vector 的区别是什么?
这两个类都实现了 List 接口( List 接口继承了 Collection 接口),他们都是有序集合
线程安全: Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非
线程安全的。
性能: ArrayList 在性能方面要优于 Vector
扩容: ArrayList Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次
会增加 1 倍,而 ArrayList 只会增加 50%
Vector 类的所有方法都是同步的。可以由两个线程安全地访问一个 Vector 对象、但是一个线程访问
Vector 的话代码要在同步操作上耗费大量的时间。
Arraylist 不是同步的,所以在不需要保证线程安全时时建议使用 Arraylist
20. 插入数据时, ArrayList LinkedList Vector 谁速度较快?阐述
ArrayList Vector LinkedList 的存储性能和特性?
ArrayList Vector 底层的实现都是使用数组方式存储数据。数组元素数大于实际存储的数据以便
增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操
作,所以索引数据快而插入数据慢。
Vector 中的方法由于加了 synchronized 修饰,因此 Vector 是线程安全容器,但性能上较
ArrayList
LinkedList 使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但插入数据时只需
要记录当前项的前后项即可,所以 LinkedList 插入速度较快
21. 多线程场景下如何使用 ArrayList
ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections synchronizedList
法将其转换成线程安全的容器后再使用。例如像下面这样:
22. 为什么 ArrayList elementData 加上 transient 修饰?
ArrayList 中的数组定义如下:
private transient Object[] elementData;
再看一下 ArrayList 的定义:
List synchronizedList = Collections.synchronizedList(list);
synchronizedList.add("aaa");
synchronizedList.add("bbb");
for (int i = 0; i < synchronizedList.size(); i++) {
System.out.println(synchronizedList.get(i));
}
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable 可以看到 ArrayList 实现了 Serializable 接口,这意味着 ArrayList 支持序列化。 transient 的作用
是说不希望 elementData 数组被序列化,重写了 writeObject 实现:
每次序列化时,先调用 defaultWriteObject() 方法序列化 ArrayList 中的非 transient 元素,然后
遍历 elementData ,只序列化已存入的元素,这样既加快了序列化的速度,又减小了序列化之后
的文件大小。
23. List Set 的区别
List , Set 都是继承自 Collection 接口
List 特点:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多
null 元素,元素都有索引。常用的实现类有 ArrayList LinkedList Vector
Set 特点:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一
null 元素,必须保证元素唯一性。 Set 接口常用实现类是 HashSet LinkedHashSet 以及
TreeSet
另外 List 支持 for 循环,也就是通过下标来遍历,也可以用迭代器,但是 set 只能用迭代,因为他无
序,无法用下标来取得想要的值。
Set List 对比
Set :检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List :和数组类似, List 可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起
其他元素位置改变
Set 接口
24. 说一下 HashSet 的实现原理?
HashSet 是基于 HashMap 实现的, HashSet 的值存放于 HashMap key 上, HashMap value
一为 present ,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层
HashMap 的相关方法来完成, HashSet 不允许重复的值。
25. HashSet 如何检查重复? HashSet 是如何保证数据不可重复的?
HashSet add () 元素时,判断元素是否存在的依据,不仅要比较 hash 值,同时还要结合
equles 方法比较。
HashSet 中的 add () 方法会使用 HashMap put() 方法。
private void writeObject(java.io.ObjectOutputStream s) throws
java.io.IOException{
*// Write out element count, and any hidden stuff*
int expectedModCount = modCount;
s.defaultWriteObject();
*// Write out array length*
s.writeInt(elementData.length);
*// Write out all elements in the proper order.*
for (int i=0; i
s.writeObject(elementData[i]);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
} HashMap
HashSet
实现了 Map 接口
实现 Set 接口
存储键值对
仅存储对象
调用 put ()向 map
添加元素
调用 add ()方法向 Set 中添加元素
HashMap 使用键
Key )计算
Hashcode
HashSet 使用成员对象来计算 hashcode 值,对于两个对象来说
hashcode 可能相同,所以 equals()
锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章