java集合框架八股文

260 阅读11分钟

1、常用的集合类有哪些?

Map接口和Collection接口是所有集合框架的父接口

(1)Collection接口的子接口包括:Set接口和List接口

(2)Map接口的实现类有:HashMap、TreeMap、HashTable、ConcurrentHashMap以及Properties

(3)Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等

(4)List接口的实现类主要有:ArrayList、LinkedList、stack以及Vector等

2、List、Set、Map三者的区别?

(1)List集合允许存储重复元素,而Set集合不能存储重复元素;

(2)List的底层是可扩容的数组,而Set集合底层是有Map的实现类实现的;

(3)Map集合是以键值对的形式存储元素得到,而List就是一个可扩容的数组

(4)List是一个有序集合、set和map是无序集合

3、集合框架底层数据结构

Collection:

1、List

1)ArrayList:Object数组
   
   (2)Vector:Object数组
   
   (3)LinkedList:双向循环链表

2、Set

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

3、Map

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

4、哪些集合类是线程安全的?

1)Vector:就比ArrayList多个synchronized(线程安全),因为效率较低,现在已经不建议使用
(2)HashTable:就比HashMap多了个synchronized(线程安全),不建议使用
 (3)ConcurrentHashMap:是Java5中支持高并发、高吞吐量的线程安全HashMap实现。它由 Segment数组结构和HashEntry数组结构组成。

5、遍历一个List有哪些不同的方式

1for循环遍历
(2)迭代器遍历
(3foreach循环遍历

6、说一下ArrayList的优缺点

优点
   ArrayList底层以数组实现,是一点随机访问模式
   ArrayList实现了RandomAccess接口,因此查找的时候非常快
   ArrayList在顺序添加一个元素的时候非常方便
缺点
   删除元素的时候,需要做一次元素复制操作,如果复制的元素很多,那么就会比较耗费性能
   插入元素的时候,也需要做一次元素复制操作

7、如何实现数组和List之间的转换

1)数组转List:使用Arrays.asList(array)进行转换
   (2)List转数组:使用List自带的toArray()方法

8、ArrayList和LinkedList的区别是什么?

  1、数据结构实现:ArrayList是动态数组的数据结构实现,而LinkedList是双向链表的数据结构实现。
  2、随机访问效率:ArrayList比LinkedList在随机访问的时候效率要高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找
  3、增加和删除效率:在非首尾的增加和删除操作,LinkedList要比ArrayList效率要高,因为ArrayList增删操作要影响数组内的其他数据的下标
  4、内存空间占用:LinkedList比ArrayList更占内存,因为LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素
  5、线程安全:ArrayList和LinkedList都是不同步的,也就是不保证线程安全

9、多线程场景下如何使用ArrayList

ArrayList不是线程安全的,如果遇到多线程场景,可以通过Collections的synchronizedList方法将其转换成线程安全的容器后再使用

10、说一下HashSet的实现原理?

HashSet是基于HashMap实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为present,因此HashMap的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成,HashSet不允许重复的值

11、HashSet如何检查重复?HashSet是如任何保证数据不可重复的?

向HashSet中add()元素时,判断元素是否存在的依据,不仅仅要比较hash值,同时还要结合equals方法比较;
HashSet中的add()方法会使用HashMap的put()方法
HashMap的key是唯一的,由源码可以看出HashSet添加进去的值就是作为HashMap的key,并且在HashMap中如果K/V相同时,会用新的V覆盖掉旧的V,然后返回旧的V。所以不会重复( HashMap 比较key是否相等是先比较hashcode 再比较equals )。

12、HashSet与HashMap的区别

image.png

13、什么是Hash算法

哈希算法是指把任意长度的二进制映射为固定长度较小的二进制值,这个较小的二进值叫做哈希值

14、什么是链表?

(1)链表是可以将物理地址上不连续的数据连接起来,通过指针来对物理地址进行操作,实现增删改查等功能。 链表大致分为单链表和双向链表 (1)单链表:每个节点包含两个部分,一部分存放数据域,另一部分是指向下一个结点的next指针 (2)双向链表:除了包含单链表的部分,还增加的pre前一个节点的指针 链表的优点 插入删除速度快,内存利用率高,不会浪费内存、大小没有固定 链表的缺点: 不能随机查找,必须从第一个开始遍历,查找效率低

15、说一下HashMap的实现原理

HashMap概述:HashMap是基于哈希表的Map接口的非同步实现,不是线程安全的,允许键值为null, HashMap的数据结构:1.7数组+链表,1.8数组+链表+红黑树 HashMap是基于Hash算法实现的:

1、当我们往HashMap中put元素时,利用key的hashCode重新hash计算出当前对象的元素在数组中的下标

2、存储时,如果出现哈希冲突,此时有两种情况:如果key相同,则覆盖原值,如果key不相同(出现冲突),则将当前的key—value放入链表中

3、获取时,直接找到hash值对应的下标,在进一步判读key是否相同,从而找到对应的值

16、HashMap在jdk1.7和Jdk1.8有哪些不同?HashMap的底层实现

jdk1.8之前HashMap底层是采用数组+链表实现,jdk1.8以后是采用数组+链表+红黑树,

红黑树: 红黑树是一种含有红黑结点并能自平衡的二叉查找树,

红黑树的性质:

 (1)每个节点要么是黑色,要么是红色
 (2)根结点是黑色
 (3)每个叶子结点是黑色
 (4)每个红色结点的两个子结点一定都是黑色的

扩展:jdk1.7与jdk1.8比较

1、resize扩容优化

2、引入了红黑树,目的是避免单链表过长影响查询效率

3、解决了多线程死循环问题,但是仍然是非线程安全的,多线程时可能会造成数据丢失问题

17、HashMap的put方法的具体流程

jdk1.7当我们put的时候,首先计算key的hash值,这里调用了key.hashCode()方法,hash方法的作用就是高于16bit不变,低于16bit和高16bit做一个异或,目的是减少碰撞。jdk1.8加入红黑树,提升了碰撞下的性能

18、HashMap的扩容操作是怎么实现的

1、在jdk1.8中,resize方法是在HashMap中的键值对大于阀值时,或初始化时,就调用resize方法进行扩容

2、每次扩容的时候,都是扩容2倍

3、扩容后的Node对象的位置要么在原位置,要么移动到原偏移量的两倍位置

19 HashMap是怎么解决哈希冲突的

什么是哈希 Hash,一般翻译为“散列”,hash是指利用hash算法把任意长度的二进制映射为固定长度的较小的二进制值,这个较小的二进制叫做哈希值 什么是哈希冲突 当两个key的hash值相同,我们就把他叫做哈希碰撞 HashMap是使用了哪些方法来有效解决哈希冲突的: (1)拉链法 (2)开放地址法

20、HashMap的长度为什么是2的幂次方

为了能让HashMap存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同

21、HashMap和ConcurrentHashMap的区别

1、ConcurrentHashMap对整个桶数组进行分割分段(Segment),然后在每一个分段上都用Lock锁进行保护 ,相对HashTable的synchronized锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全

2、HashMap的键值对允许有null,而ConcurrentHashMap都不允许

21、ConcurrentHashMap和HashTable的区别

ConcurrentHashMap和HashTable的区别主要体现在实现线程安全的方式上不同 底层数据结构:

Jdk1.7的ConcurrentHashMap底层采用分段数组+链表实现

Jdk1.8采用的数据结构根HashMap1.8的结构一样,数组+链表/红黑二叉树

HashTable和jdk1.8之前的HashMap的底层数据结构类似都是采用数组+链表的形式,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的;

实现线程安全的方式: (1)在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段 (Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就 不会存在锁竞争,提高并发访问率。(默认分配16个Segment,比Hashtable效率提高16 倍。) 到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑 树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在 JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;

(2) Hashtable(同一把锁) : 使用 synchronized 来保证线程安全,效率非常低下。当一个线程 访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加 元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。

22、ConcurrentHashMap底层具体实现?

 (1)jdk1.7首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问
 (2)在jdk1.7中,ConcurrentHashMap采用Segment+HashEntry的方式进行实现
 (3)一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构,一个Segment包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里面的元素,当对HashEntry数组的数据进行修改时,必须首先获得对应的Segment的锁
 (4)Segment是一种可重入的锁ReentrantLock,每个Segment守护一个HashEntry数组里面的元素,当对HashEntry数组的数据进行修改时,必须首先获得对应的Segment锁。

23、什么是TreeMap

TreeMap是一个有序的key——value集合,它是通过红黑树实现的,TreeMap基于红黑树实现,该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的Comparator进行排序的,具体取决于使用的构造方法,TreeM是线程非同步的

24、Comparable和comparator的区别

 comparable接口实际上是出自java.lang包,它有一个 compareTo(Object obj)方法用来排序
 comparator接口实际上是出自 java.util 包,它有一个compare(Object obj1, Object obj2)方法用 来排序