《天净沙·我·Java集合》 双非菜鸡奇葩,面试项目框架,java java,卑微学子去哪?


别问 问就是为了面试豁出了老命
[文章都是搬运艿艿的]
[http://svip.iocoder.cn/Java/Core/Interview/]

Java集合概述图

概述图

说出一些集合框架的优点?

  1. 使用核心集合类降低开发成本,而非实现我们自己的集合类。
  2. 随着使用经过严格测试的集合框架类,代码质量会得到提高。
  3. 通过使用 JDK 附带的集合类,可以降低代码维护成本。
  4. 复用性和可操作性。

    集合框架中的泛型有什么优点?

    Java5 引入了泛型,所有的集合接口和实现都大量地使用它。泛型允许我们为集合提供一个可以容纳的对象类型。因此,如果你添加其它类型的任何元素,它会在
    编译时报错。这避免了在运行时出现 ClassCastException,因为你将会在编译时得到报错信息。

泛型也使得代码整洁,我们不需要使用显式转换和 instanceOf 操作符。它也给运行时带来好处,因为不会产生类型检查的字节码指令

Java 集合框架的基础接口有哪些?

Collection

为集合层级的根接口。一个集合代表一组对象,这些对象即为它的元素。Java 平台不提供这个接口任何直接的实现。

Set

是一个不能包含重复元素的集合。这个接口对数学集合抽象进行建模,被用来代表集合,就如一副牌。

List

是一个有序集合,可以包含重复元素。你可以通过它的索引来访问任何元素。List 更像长度动态变换的数组。

Map

是一个将 key 映射到 value 的对象。一个 Map 不能包含重复的 key,每个 key 最多只能映射一个 value 。

其它

一些其它的接口有 Queue、Dequeue、SortedSet、SortedMap 和 ListIterator 。

为何 Collection 不从 Cloneable 和 Serializable 接口继承?

Collection 接口指定一组对象,对象即为它的元素
如何维护这些元素由 Collection 的具体实现决定。例如,一些如 List 的 Collection 实现允许重复的元素,而其它的如 Set 就不允许。
很多 Collection 实现有一个公有的 clone 方法。然而,把它放到集合的所有实现中也是没有意义的。这是因为 Collection 是一个抽象表现,重要的是实现。

为何 Map 接口不继承 Collection 接口?

尽管 Map 接口和它的实现也是集合框架的一部分,但 Map 不是集合,集合也不是 Map。因此,Map 继承 Collection 毫无意义,反之亦然。
如果 Map 继承 Collection 接口,那么元素去哪儿?Map 包含 key-value 对,它提供抽取 key 或 value 列表集合( Collection )的方法,但是它不适合
“一组对象”规范。

Collection 和 Collections 的区别?

Collection ,是集合类的上级接口,继承与他的接口主要有 Set 和List 。
Collections ,是针对集合类的一个工具类,它提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

集合框架里实现的通用算法有哪些?

Java 集合框架提供常用的算法实现,比如排序和搜索。
Collections类包含这些方法实现。大部分算法是操作 List 的,但一部分对所有类型的集合都是可用的。部分算法有排序、搜索、混编、最大最小值。

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

List

  1. ArrayList :Object 数组。
  2. Vector :Object 数组。
  3. LinkedList :双向链表(JDK6 之前为循环链表,JDK7 取消了循环)。

Map

  1. HashMap :
    JDK8 之前,HashMap 由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。
    JDK8 以后,在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8 )时,将链表转化为红黑树,以减少搜索时间。
  2. LinkedHashMap :LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上
    面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查
    看:《LinkedHashMap 源码详细分析(JDK1.8)》 。
  3. Hashtable :数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。
  4. TreeMap :红黑树(自平衡的排序二叉树)。

Set

HashSet :无序,唯一,基于 HashMap 实现的,底层采用 HashMap 来保存元素。
LinkedHashSet :LinkedHashSet 继承自 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 HashMap 实现一样,不过还是有一点点区别的。
TreeSet :有序,唯一,红黑树(自平衡的排序二叉树)。

什么是迭代器(Iterator)?

Iterator 接口,提供了很多对集合元素进行迭代的方法。每一个集合类都包含了可以返回迭代器实例的迭代方法。迭代器可以在迭代的过程中删除底层集合的元
素,但是不可以直接调用集合的 #remove(Object Obj) 方法删除,可以通过迭代器的 #remove() 方法删除。

Iterator 和 ListIterator 的区别是什么?

Iterator 可用来遍历 Set 和 List 集合,但是 ListIterator 只能用来遍历 List。
Iterator 对集合只能是前向遍历,ListIterator 既可以前向也可以后向。
ListIterator 实现了 Iterator 接口,并包含其他的功能。比如:增加元素,替换元素,获取前一个和后一个元素的索引等等。

快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

差别在于 ConcurrentModification 异常:
快速失败:当你在迭代一个集合的时候,如果有另一个线程正在修改你正在访问的那个集合时,就会抛出一个 ConcurrentModification 异常。 在 java.util 包下的都是快速失败。
安全失败:你在迭代的时候会去底层集合做一个拷贝,所以你在修改上层集合的时候是不会受影响的,不会抛出 ConcurrentModification 异常。在 java.util.concurrent 包下的全是安全失败的。

如何删除 List 中的某个元素?

方式一,使用 Iterator ,顺序向下,如果找到元素,则使用 remove 方法进行移除。
方式二,倒序遍历 List ,如果找到元素,则使用 remove 方法进行移除。

Enumeration 和 Iterator 接口有什么不同?

Enumeration 跟 Iterator 相比较快两倍,而且占用更少的内存。
但是,Iterator 相对于 Enumeration 更安全,因为其他线程不能修改当前迭代器遍历的集合对象。同时,Iterators 允许调用者从底层集合中移除元素,这些 Enumerations 都没法完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.Vector;
import java.util.Enumeration;

public class EnumerationTester {

public static void main(String args[]) {
Enumeration<String> days;
Vector<String> dayNames = new Vector<String>();
dayNames.add("Sunday");
dayNames.add("Monday");
dayNames.add("Tuesday");
dayNames.add("Wednesday");
dayNames.add("Thursday");
dayNames.add("Friday");
dayNames.add("Saturday");
days = dayNames.elements();
while (days.hasMoreElements()){
System.out.println(days.nextElement());
}
}
}

为何 Iterator 接口没有具体的实现?

Iterator 接口,定义了遍历集合的方法,但它的实现则是集合实现类的责任。每个能够返回用于遍历的 Iterator 的集合类都有它自己的 Iterator 实现内部类。
这就允许集合类去选择迭代器是 fail-fast 还是 fail-safe 的。比如,ArrayList 迭代器是 fail-fast 的,而 CopyOnWriteArrayList 迭代器是 fail-safe 的。

Comparable 和 Comparator 的区别?

Comparable 接口,在 java.lang 包下,用于当前对象和其它对象的比较,所以它有一个 #compareTo(Object obj) 方法用来排序,该方法只有一个参数。
Comparator 接口,在 java.util 包下,用于传入的两个对象的比较,所以它有一个 #compare(Object obj1, Object obj2) 方法用来排序,该方法有两个参数。

compareTo 方法的返回值表示的意思?

· 大于 0 ,表示对象大于参数对象。
· 小于 0 ,表示对象小于参数对象
· 等于 0 ,表示两者相等。

如何对 Object 的 List 排序?

对 Object[] 数组进行排序时,我们可以用 Arrays#sort(…) 方法。
对 List 数组进行排序时,我们可以用 Collections#sort(…) 方法。

List VS Set

List 和 Set 区别?

List,Set 都是继承自 Collection 接口。
· List 特点:元素有放入顺序,元素可重复。
· Set 特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉。

注意:元素虽然无放入顺序,但是元素在 Set 中的位置是有该元素的 hashcode 决定的,其位置其实是固定的。
另外 List 支持 for 循环,也就是通过下标来遍历,也可以用迭代器,但是 Set 只能用迭代,因为他无序,无法用下标来取得想要的值。

Set 和 List 对比:

Set:检索指定的元素效率高,删除和插入效率高,插入和删除可能会引起元素位置改变。
List:和数组类似,List 可以动态增长,查找指定的元素效率低,插入删除指定的元素效率低,因为可能会引起其他元素位置改变。

Array系 VS List系

Array 和 ArrayList 有何区别?什么时候更适合用 Array?

Array 可以容纳基本类型和对象,而 ArrayList 只能容纳对象。
Array 是指定大小的,而 ArrayList 大小是固定的,可自动扩容。
Array 没有提供 ArrayList 那么多功能,比如 addAll、removeAll 和 iterator 等。
尽管 ArrayList 明显是更好的选择,但也有些时候 Array 比较好用,比如下面的三种情况。

1、如果列表的大小已经指定,大部分情况下是存储和遍历它们
2、对于遍历基本数据类型,尽管 Collections 使用自动装箱来减轻编码任务,在指定大小的基本类型的列表上工作也会变得很慢。
3、如果你要使用多维数组,使用 [][] 比 List 会方便。

ArrayList 与 LinkedList 区别?

ArrayList

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

LinkedList

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

适用场景分析:

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

ArrayList 是如何扩容的?

如果通过无参构造的话,初始数组容量为 0 ,当真正对数组进行添加时,才真正分配容量。每次按照 1.5 倍(位运算)的比率通过 copeOf 的方式扩容。
在 JKD6 中实现是,如果通过无参构造的话,初始数组容量为10,每次通过 copeOf 的方式扩容后容量为原来的 1.5 倍。

ArrayList 与 Vector 区别?

rrayList 和 Vector 都是用数组实现的,主要有这么三个区别:

  1. Vector 是多线程安全的,线程安全就是说多线程访问同一代码,不会产生不确定的结果,而 ArrayList 不是。这个可以从源码中看出,Vector 类中的方法很多有 synchronized 进行修饰,这样就导致
    了 Vector 在效率上无法与 ArrayList 相比。
  2. 两个都是采用的线性连续空间存储元素,但是当空间不足的时候,两个类的增加方式是不同。
  3. Vector 可以设置增长因子,而 ArrayList 不可以

Vector 是线程同步的,所以它也是线程安全的,而 ArrayList 是线程无需同步的,是不安全的。如果不考虑到线程的安全因素,一般用 ArrayList 效率比较高。
实际场景下,如果需要多线程访问安全的数组,使用 CopyOnWriteArrayList 。
如果集合中的元素的数目大于目前集合数组的长度时,在集合中使用数据量比较大的数据,用 Vector 有一定的优势。

HashMap 和 Hashtable 的区别?

  1. Hashtable 是在 Java 1.0 的时候创建的,而集合的统一规范命名是在后来的 Java2.0 开始约定的,而当时其他一部分集合类的发布构成了新的集合框架。
    Hashtable 继承 Dictionary ,HashMap 继承的是 Java2 出现的 Map 接口。
  2. HashMap 去掉了 Hashtable 的 contains 方法,但是加上了 containsValue 和 containsKey 方法。
  3. HashMap 允许空键值,而 Hashtable 不允许。
    【重点】4. HashTable 是同步的,而 HashMap 是非同步的,效率上比 HashTable 要高。也因此,HashMap 更适合于单线程环境,而 HashTable 适合于多线程环境。
  4. HashMap 的迭代器(Iterator)是 fail-fast 迭代器,HashTable的 enumerator 迭代器不是 fail-fast 的。
  5. HashTable 中数组默认大小是 11 ,扩容方法是 old * 2 + 1 ,HashMap 默认大小是 16 ,扩容每次为 2 的指数大小。
    即使在多线程环境下,现在也有同步的 ConcurrentHashMap 替代,没有必要因为是多线程而用 Hashtable

HashSet 和 HashMap 的区别?

Set 是线性结构,值不能重复。HashSet 是 Set 的 hash 实现,HashSet 中值不能重复是用 HashMap 的 key 来实现的。
Map 是键值对映射,可以空键空值。HashMap 是 Map 的 hash 实现,key 的唯一性是通过 key 值 hashcode 的唯一来确定,value 值是则是链表结构

HashSet 和 TreeSet 的区别?

HashSet 是用一个 hash 表来实现的,因此,它的元素是无序的。添加,删除和 HashSet 包括的方法的持续时间复杂度是 O(1) 。
TreeSet 是用一个树形结构实现的,因此,它是有序的。添加,删除和 TreeSet 包含的方法的持续时间复杂度是 O(logn) 。

如何决定选用 HashMap 还是 TreeMap?

对于在 Map 中插入、删除和定位元素这类操作,HashMap 是最好的选择。
然而,假如你需要对一个有序的 key 集合进行遍历, TreeMap 是更好的选择。

HashMap 和 ConcurrentHashMap 的区别?

ConcurrentHashMap 是线程安全的 HashMap 的实现。主要区别如下:

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

HashMap 的工作原理是什么?

HashMap

我们知道在 Java 中最常用的两种结构是数组和模拟指针(引用),几乎所有的数据结构都可以利用这两种来组合实现,HashMap 也是如此。实际上 HashMap 是一个“链表散列”。
HashMap 是基于 hashing 的原理。

当两个对象的 hashCode 相同会发生什么?

因为 hashcode 相同,所以它们的 bucket 位置相同,“碰撞”会发生。
因为 HashMap 使用链表存储对象,这个 Entry(包含有键值对的 Map.Entry 对象)会存储在链表中。

HashMap 默认容量是多少?

默认容量都是 16 ,负载因子是 0.75 。就是当 HashMap 填充了 75% 的 busket 是就会扩容,最小的可能性是(16 * 0.75 = 12),一般为原内存的 2 倍。