集合框架知识点总结+大厂面试题

Collection:集合List,Set,Queue的最基本的接口

Map:映射表的基础接口

Iterator:迭代器,可以遍历集合中的数据

(注意:下图有个错误,ArrayList扩增时,容量扩增为1.5倍)

List

List是有序的Collection。Java List一共三个实现类: 分别是ArrayList、Vector和LinkedList。 

ArrayList是最常用的List的实现类,内部通过数组实现的,,适合随即查找和遍历,不适合插入和删除(代价较大)

实现原理:数组,当容量不够时,创建新的容量数组,使用arraycopy将原来的数组复制过去

Vector

和ArrayList一样是通过数组实现的,不同之处在于,支持线程的同步,同一时间只能有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此访问较慢。

LinkedList

使用链表存储数据,适合动态插入和删除,随机访问和遍历速度较慢,另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆 栈、队列和双向队列使用。

Set

Set是存储无序,不可重复。对象的相等本质是hashCode值(java根据内存地址计算出来的序号)

HashSet(哈希表)

HashSet中的存储元素,不是按照存入的顺序,而是按照hash值取得,元素的hash值通过hashcod方法获取的。 HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较 equals方法 如果 equls结果为true ,HashSet就视为同一个元素。如果equals 为false就不是 同一个元素。 
哈希值相同的在同样的hash值下顺延,如图:

HashSet通过hashCode值来确定元素在内存中的位置。一个hashCode位置上可以存放多个元 素

TreeSet(二叉树)

TreeSet()是使用二叉树的原理对新add()的对象按照指定的顺序排序(升序、降序),每增 加一个对象都会进行排序,将对象插入的二叉树指定的位置。(排列无序,不可重复)

LinkHashSet(HashSet+LinkedHashMap)

它继承与 HashSet、又基于 LinkedHashMap 来实现的。 LinkedHashSet 底层使用 LinkedHashMap 来保存所有元素,它继承与 HashSet,其所有的方法 操作上又与HashSet相同,因此LinkedHashSet 的实现上非常简单,只提供了四个构造方法,并 通过传递一个标识参数,调用父类的构造器,底层构造一个 LinkedHashMap 来实现,在相关操 作上与父类HashSet的操作相同,直接调用父类HashSet的方法即可。

Map

HashMap(数组+链表+红黑树)

HashMap根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快 的访问速度,但遍历顺序却是不确定的。 HashMap多只允许一条记录的键为null,允许多条记 录的值为 null。HashMap 非线程安全,即任一时刻可以有多个线程同时写 HashMap,可能会导 致数据的不一致。如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使 HashMap 具有线程安全的能力,或者使用 ConcurrentHashMap。我们用下面这张图来介绍 HashMap 的结构:

HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。上图中,每个绿色 的实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。

1. capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。

2. loadFactor:负载因子,默认为 0.75。 

3. threshold:扩容的阈值,等于 capacity * loadFactor

JAVA8的实现

Java8 对 HashMap 进行了一些修改,大的不同就是利用了红黑树,所以其由 数组+链表+红黑 树 组成。 
根据 Java7 HashMap 的介绍,我们知道,查找的时候,根据 hash 值我们能够快速定位到数组的 具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决 于链表的长度,为 O(n)。为了降低这部分的开销,在 Java8 中,当链表中的元素超过了 8 个以后, 会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。 
 
 

HashTable(线程安全)

Hashtable 是遗留类,很多映射的常用功能与 HashMap 类似,不同的是它承自 Dictionary 类, 并且是线程安全的,任一时间只有一个线程能写 Hashtable,并发性不如 ConcurrentHashMap, 因为 ConcurrentHashMap 引入了分段锁。Hashtable 不建议在新代码中使用,不需要线程安全 的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换。 

TreeMap(可排序)

TreeMap 实现 SortedMap 接口,能够把它保存的记录根据键排序,默认是按键值的升序排序, 也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。 
如果使用排序的映射,建议使用TreeMap。 
在使用 TreeMap 时,key 必须实现 Comparable 接口或者在构造 TreeMap 传入自定义的 Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常

LinkHashMap(记录插入顺序)

LinkedHashMap 是 HashMap 的一个子类,保存了记录的插入顺序,在用 Iterator 遍历 LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。 

面试题

1. ArrayList 和 Vector 的区别

两个类都实现了List接口,他们都是有序集合,可以重复。

两者区别主要在两个部分:

同步性:Vector线程安全,说明他线程是同步的,而ArrayList是线程不安全,不同步的。只有一个线程访问的时候,使用ArrayList,不考虑线程安全,效率更高;多个线程访问集合,使用Vecctor不用考虑,编写线程安全的代码

数据增长:两者都有一个初始容量大小,当容量不足时,Vector每次增长为原来的两倍,ArrayList默认增长为原来的1.5倍。ArrayList 与 Vector 都可以设置初始的空间大小,Vector 还可以设置增长的
空间大小,而 ArrayList 没有提供设置增长空间的方法

2. 说说 ArrayList,Vector, LinkedList 的存储性能和特性

ArrayList 和 Vector 都是使用数组方式存储数据,此数组元素数大于实际存储的数
据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组
元素移动等内存操作,所以索引数据快而插入数据慢,Vector 由于使用了 
synchronized 方法(线程安全)。 
通常性能上较 ArrayList 差,而 LinkedList 使用双向链表实现存储,按序号索引数
据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插
入速度较快 。 
ArrayList 在查找时速度快,LinkedList 在插入与删除时更具优势。 

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

Iterator 的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。
java.util 包下面的所有的集合类都是快速失败的,而 java.util.concurrent 包下面的
所有的类都是安全失败的。快速失败的迭代器会抛出 
ConcurrentModificationException 异常,而安全失败的迭代器永远不会抛出这样的
异常

4.hashmap的数据结构

(java编程语言中数据结构只有数组和链表)HashMap中采用数组和链表的结合体,称为链表散列(hashing)

5. HashMap 的工作原理是什么?

HashMap是 用键值对(key-value)的形式来存储元素的。HashMap需要一个hash函数,它使用hashCode()和equals()方法向集合添加和检索元素。调用put方法,hashMap会计算key的hash值,然后把键值对存储在合适的索引中,key不在,value会更新成新值。HashMap 的一些重要的特性是它的容量 (capacity),负载因子 (load factor) 和扩
容极限(threshold resizing)。

6. Hashmap 什么时候进行扩容呢?

当hashmap的元素个数超过数组大小的loadFactor时,进行数组扩容,loadFactor默认是0.75,默认数组大小为16,当个数超过12就会扩展为原来的两倍,然后重新计算每个元素在数组中的位置。

7. List、Map、Set 三个接口,存取元素时,各有什么特点

首先List和Set都是单列元素的集合,他们的父接口都是Collection。Set不允许有重复的元素,即不能有两个相等的元素(equals),所以set集合的方法有一个boolean的返回值,判断是否相等,Set集合是无序的,不能任意取出,可以使用Iterator遍历各个元素。List表示有顺序的集合,按照add添加的顺序,让指定的索引指向整个对象,可以任意取出某个对象。

其次Map和List,Set都不同,她是双列的集合,使用put方法存储key/value,不能存储相同的key值,重复的规则也是用equals判断,同时可以单独取出全部的key值或者value值,也可以获得组合值(Map.Entry)。

List 以特定次序来持有元素,可有重复元素。Set 无法拥有重复元素, 内部排序。
Map 保存 key-value 值,value 可多值

(HashSet 按照 hashcode 值的某种运算方式进行存储,而不是直接按 hashCode 
值的大小进行存储。)

8. Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用 == 还是 
equals()? 它们有何区别?

判断元素是否重复,使用equals判断。

  1.  ==是判断两个变量或实例是不是指向同一个内存空间,equals是判断两个变量或实例所指向的内存空间的值是不是相同 
  2. ==是指对内存地址进行比较 , equals()是对字符串的内容进行比较
  3. ==指引用是否相同, equals()指的是值是否相同

9. 两个对象值相同 (x.equals(y) == true),但却可有不同的 hash code,这句话对不对? 

对。如果对象要保存在 HashSet 或 HashMap 中,它们的 equals 相等,那么,它
们的 hashcode 值就必须相等。 
如果不是要保存在 HashSet 或 HashMap,则与 hashcode 没有什么关系了,这时
候 hashcode 不等是可以的,例如 arrayList 存储的对象就不用实现 hashcode,当
然,我们没有理由不实现,通常都会去实现的

10. heap 和 stack 有什么区别

java内存分为栈内存和堆内存,栈内存是指程序进入某个方法后,为这个方法单独开辟一块存储空间,用来存储方法中的局部变量,方法结束后,释放该内存。堆内存用来存储不放在方法栈的数据,例如new的对象。局部变量使用final修饰后放在堆里。

11. Java 集合类框架的基本接口有哪些?

java集合类中最基本的接口有:

Collection:代表一组对象,每一个对象都是它的子元素。 
Set:不包含重复元素的 Collection。 
List:有顺序的 collection,并且可以包含重复元素。 
Map:可以把键 (key) 映射到值 (value) 的对象,键不能重复

12. HashSet 和 TreeSet 有什么区别?

HashSet使用hash表来实现的,元素无序,放入的内容不能重复,只能放入一次null

TreeSet 是由一个二叉树的结构来实现的,它里面的元素是有序的,不能放入null

13. HashSet 的底层实现是什么? 

HashSet的实现依赖于HashMap,hashSet的构造法会初始化一个hashMap对象。HashSet 不允许值重复,因此,HashSet 的值是作为 HashMap 的 key 存储在HashMap 中的,当存储的值已经存在时返回 false

14. LinkedHashMap 的实现原理?

LinkedHashMap也是基于HashMap实现的,不同的是它定义了一个Entry header,这个header不是放在table里,额外独立出来的,LinkedHashMap 通过继承 hashMap 中的 Entry, 并添加两个属性 Entry before,after, 和 header 结合起来组成一个双向链表来实现按插入顺序或访问顺序排序。

LinkedHashMap 定义了排序模式 accessOrder,该属性为 boolean 型变量,对于访问顺序,为 true;对于插入顺序,则为false。一般情况下,不必指定排序模式,其迭代顺序即为默认为插入顺序。 

15. 为什么集合类没有实现 Cloneable 和 Serializable 接口

克隆 (cloning) 或者是序列化 (serialization) 的语义和含义是跟具体的实现相关的。
因此,应该 由集合类的具体实现来决定如何被克隆或者是序列化

16. 什么是迭代器 (Iterator)

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

17. Iterator 和 ListIterator 的区别是什么

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

18. 数组 (Array) 和列表 (ArrayList) 有什么区别?什么时候应该使用 Array 而不是 ArrayList?

Array可以包含基本类型和对象类型,ArrayList只能包含对象类型,Array大小是固定的,ArrayList是动态变化的,

ArrayList处理固定大小的基本数据类型时,速度较慢。

19. Java 集合类框架的最佳实践有哪些?

 假如元素的大小是固 定的,而且能事先知道,我们就应该用 Array 而不是ArrayList。 
 有些集合类允许指定初始容量。因此,如果我们能估计出存储的元素的数目,我们可以设置 初始容量来避免重新计算 hash 值或者是扩容。 
 为了类型安全,可读性和健壮性的原因总是要使用泛型。同时,使用泛型还可以避免运行时的 ClassCastException。 
 使用 JDK 提供的不变类 (immutable class) 作为 Map 的键可以避免为我们自己的类实现 hashCode()和 equals()方法。 
 编程的时候接口优于实现。 
 底层的集合实际上是空的情况下,返回长度是 0 的集合或者是数组,不要返回 null

20.Comparable 和 Comparator 接口是干什么的?列出它们的区别

Comparable接口只提供了一个方法compareTo(),这个方法可以给两个对象排序,具体来说,她返回负数,0,正数来表明输入对象小于,等于,大于已经存在的对象。Comparator提供了compare()和equals()方法,前者用来给两个输入参数排序,返回负数,0,正数分别代表小于,等于,大于。equals()方法需要要个对象作为参数,它用来决定输入参数是否和 comparator 相等。只有当输入参数也是一个 comparator 并且输入参数和当前 comparator 的排序结果是相同的时 候,这个方法才返回 true。 

21.Collection 和 Collections 的区别

collection 是集合类的上级接口, 继承与它的接口主要是 set 和 list。 
collections 类是针对集合类的一个帮助类. 它提供一系列的静态方法对各种集合的搜
索, 排序, 线程安全化等操作

猜你喜欢

转载自blog.csdn.net/qq_40513633/article/details/107258744