首先要说一下,本文对这些 Java 集合框架的面试题只做了一个总结式的回答,对每一道题目,都值得深入去了解一下(什么是扎实基本功,这些就是基本功~~),后续可能对每一道题目拆开独立篇章来深入讲解一下。
大家看到这些总结,有疑惑的,就赶紧去查一查深入了解一下,当然也欢迎指出文中错误之处。
以下是大纲:
■ HashMap 和 HashTable 的区别?
■ 说一下 HashMap 的底层结构?
■ ArrayList 和 LinkedList 的区别是什么?
■ ArrayList 和 Vector 的区别是什么?
■ Array 和 ArrayList 有何区别?
■ 说一下 HashSet 的实现原理?
■ List、Set、Map 之间的区别是什么?
HashMap 和 HashTable 的区别?
HashMap 不是线程安全的
HashMap 是 map 接口的实现类,是将键映射到值的对象,其中键和值都是对象,并且不能包含重复键,但可以包含重复值。HashMap 允许 null key 和 null value,而 HashTable 不允许。
HashTable 是线程安全 Collection。
HashMap 是 HashTable 的轻量级实现,他们都完成了 Map 接口,主要区别在于 HashMap 允许 null key 和 null value, 由于非线程安全,效率上可能高于 Hashtable。
区别:
■ HashMap 允许将 null 作为一个 entry 的 key 或者 value,而 Hashtable 不允许。
■ HashMap 把 Hashtable 的 contains 方法去掉了,改成 containsValue 和 containsKey。因为 contains 方法容易让人引起误解。
■ HashTable 继承自 Dictionary 类,而 HashMap 是 Java1.2 引进的 Map interface 的一个实现。
■ HashTable 的方法是 Synchronize 的,而 HashMap 不是,在多个线程访问 Hashtable 时,不需要自己为它的方法实现同步,而 HashMap 就必须为之提供外同步。
说一下 HashMap 的底层结构?
HashMap 的主干是一个 Entry 数组。Entry 是 HashMap 的基本组成单元,每一个 Entry 包含一个 key-value 键值对。整体结构图:
HashMap 由数组 + 链表组成的。
数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前 entry 的 next 指向 null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为 O (n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过 key 对象的 equals 方法逐一比对查找。所以,性能考虑,HashMap 中的链表出现越少,性能才会越好。
ArrayList 和 LinkedList 的区别是什么?
⒈ ArrayList 是实现了基于动态数组的数据结构,LinkedList 基于链表的数据结构。
⒉ 对于随机访问 get 和 set,ArrayList 觉得优于 LinkedList,因为 LinkedList 要移动指针。
⒊ 对于新增和删除操作 add 和 remove,LinedList 比较占优势,因为 ArrayList 要移动数据。
ArrayList 和 Vector 的区别是什么?
1. 同步性:
Vector 是线程安全的,也就是说是它的方法之间是线程同步的,而 ArrayList 是线程序不安全的,它的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用 ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用 Vector,因为不需要我们自己再去考虑和编写线程安全的代码。
PS:对于 Vector&ArrayList、Hashtable&HashMap,要记住线程安全的问题,记住 Vector 与 Hashtable 是旧的,是 java 一诞生就提供了的,它们是线程安全的,ArrayList 与 HashMap 是 java2 时才提供的,它们是线程不安全的。所以,我们讲课时先讲老的。
2. 数据增长:
ArrayList 与 Vector 都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要增加 ArrayList 与 Vector 的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。
Vector 默认增长为原来两倍,而 ArrayList 的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的 1.5 倍)。ArrayList 与 Vector 都可以设置初始的空间大小,Vector 还可以设置增长的空间大小,而 ArrayList 没有提供设置增长空间的方法。
即 Vector 增长原来的一倍,ArrayList 增加原来的 0.5 倍。
Array 和 ArrayList 有何区别?
■ Array 可以包含基本数据类型和引用类型,ArrayList 只能包含引用类型。
■ ArrayList 是基于数组实现的,Array 大小不可以调整大小,但 ArrayList 可以通过内部方法自动调整容量。
■ ArrayList 是 List 接口的实现类,相比 Array 支持更多的方法和特性。
说一下 HashSet 的实现原理?
⒈ HashSet 是基于 HashMap 实现的,默认构造函数是构建一个初始容量为 16,负载因子为 0.75 的 HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
⒉ 当我们试图把某个类的对象当成 HashMap 的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的 equals (Object obj) 方法和 hashCode () 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode () 返回值相同时,它们通过 equals () 方法比较也应该返回 true。通常来说,所有参与计算 hashCode () 返回值的关键属性,都应该用于作为 equals () 比较的标准。
⒊ HashSet 的其他操作都是基于 HashMap 的。
List、Set、Map 之间的区别是什么?
List(列表)
List 的元素以线性方式存储,可以存放重复对象,List 主要有以下两个实现类:
1.ArrayList: 长度可变的数组,可以对元素进行随机的访问,向 ArrayList 中插入与删除元素的速度慢。JDK8 中 ArrayList 扩容的实现是通过 grow( ) 方法里使用语句 newCapacity = oldCapacity + (oldCapacity >> 1)(即 1.5 倍扩容)计算容量,然后调用 Arrays.copyof( ) 方法进行对原数组进行复制。
LinkedList: 采用链表数据结构,插入和删除速度快,但访问速度慢。
Set(集合)
Set 中的对象不按特定 (HashCode) 的方式排序,并且没有重复对象,Set 主要有以下两个实现类:
1.HashSet:HashSet 按照哈希算法来存取集合中的对象,存取速度比较快。当 HashSet 中的元素个数超过数组大小 * loadFactor(默认值为 0.75)时,就会进行近似两倍扩容(newCapacity = (oldCapacity << 1) + 1)。
2.TreeSet:TreeSet 实现了 SortedSet 接口,能够对集合中的对象进行排序。
Map (映射)
Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一个键对象和值对象。Map 主要有以下实现类:
HashMap:HashMap 基于散列表实现,其插入和查询 的开销是固定的,可以通过构造器设置容量和负载因子来调整容器的性能。
LinkedHashMap:类似于 HashMap,但是迭代遍历它时,取得 的顺序是其插入次序,或者是最近最少使用 (LRU) 的次序。
TreeMap:TreeMap 基于红黑树实现。查看 时,它们会被排序。TreeMap 是唯一的带有 subMap( ) 方法的 Map,subMap( ) 可以返回一个子树。
(本文链接:https://m.zhipin.com/mpa/html/student/intro?articleId=9d1ef9e771d0716f0Hd8&lid=cd1d8dfe-3d2a-4508-98ff-089e38dfb180.overseasarticle.4
联系侵删)