集合面试题

   Java 集合包含一组接口和实现类,底层使用不同类型的数据结构,提供不同特点的存储方式,主要分为两大类:Collection单列集合和 Map键值对集合。 

  1.Collection 接口用于表示单例集合,主要包括三个子接口:List、SetQueue
List 是有序的、可重复的集合。它维护元素的插入顺序,可以包含重复元素;。主要实现类包括:
·ArrayList:基于动态数组实现,提供随机访问和插入性能;,

·LinkedList:基于双向链表实现,适合频繁插入和删除操作:
·Vector:类似 ArrayList,基于动态数组实现,使用synchronized 实现线程安全,性能略低;

·Stack:Vector类的子类,提供了FILO先进后出的线结构;
·CopyOnWriteArrayList:基于动态数组实现,使用ReentrantLock实现线程安全;


Set是不允许重复元素的集合:主要实现类包括:
·HashSet :基于HashMap 实现,元素唯一,无序;
·LinkedHashSet:Hashset的子类,基于LinkedHashMap实现,元素唯一且有序,同时保持                   了哈希表的性能;

·TreeSet:基于TreeMap 实现,元素唯一,自动排序或按照自定义规则排序;

Queue是用于对元素进行先进先出(FIFO)操作的队列集合;主要实现包括:
·LinkedList:基于双向链表实现;
LinkedBlockingQueue:线程安全的阻塞队列,适用于多线程环境;


2.Map接口用于表示键值对的集合,Map集合中的 key键是唯一的,而value值可以重复。
Map 的主要实现类包括:
·HashMap:基于哈希表实现,提供快速的查找和插入性能;
·LinkedHashMap:HashMap类的子类,内部多维护了一条双向链表,保存维护了元素的插                  入顺序;

·TreeMap:基于红黑树实现,根据key键自动排序或按照自定义规则排序;
·Hashtable :基于哈希表实现,key 和 value不允许为nul1,早期的线程安全实现类,使用                    synchronize d实现线程安全;
ConcurrentHashMap:基于哈希表实现,使用synchronized+CAS实现线程安全:

2. List,Set,Map 的区别?

·List:  集合中存储的元素是有序的,所以可以进行排序操作。另外,集合中的元素可以可重复;

·set: 集合中存储的元素不可重复的:
·Map: 集合中的元素使用键值对(Key-Value)存储,Key是无序+不可重复的,Value可重复的:

3. ArrayList 与 Vector 区别?

ArrayListVector 都是基于动态数组实现的List接口的集合实现类,它们的区别主要包括:初始容量:
ArrayList初始默认容量为0,添加第一个元素时,扩容为10

Vector 初始默认容量为10;

扩容方式:
ArrayList:在原有容量基础上,扩容0.5 倍(新容量是原有容量的1.5倍);

Vector:在原有容量基础上,扩容1倍(新容量是原有容量的2倍);,

线程安全:
ArrayList :线程不安全(可使用CopyOnWriteArrayList集合解决):

Vector :线程安全,操作方法使用synchronized(同步锁)实现线程同步;·

执行效率:
Vector 的方法都有同步锁,在方法执行时需要加锁,解锁,所以性能会低于ArrayList:

4. Arraylist 与 LinkedList 区别?

ArrayListLinkedList都是List接口的集合实现类,它们的区别主要包括:.

底层数据结构:
Arraylist :底层使用的是0bject[]数组.

LinkedList :底层使用的是双向链表;.

插入和删除元素:
ArrayList:插入删除时需要复制数组内的元素,所以性能较差;

LinkedList :插入删除时,只影响相邻节点,所以性能较高;,

查找和遍历元素:
ArrayList:插入删除时需要复制数组内的元素,所以性能较差;

LinkedList:插入删除时,只影响相邻节点,所以性能较高;.

RandomAccess接口:
使用Collections,binarySearch() 方法,基于二分查找法,进行元素查找时:
ArrayList:实现了RandonAccess 接口,使用1ndexedBinarySearch()(基于下标的二分查找),性能较好;
LinkedList:没有实现RandonAccess接口,使用iteratorBinarySearch()(基于迭代器的二分查找),迭代器会产生额外遍历操作,性能较差;

5.ArrayList的扩容机制?

构造函数初始化时
。使用无参数构造方法创建ArrayList时,内部的动态数组被初始化为一个空数组。当向数组中添加第一个元素时,数组容量扩为10
。使用有参数构造方法创建ArrayList时,内部的动态数组按照指定容量进行初始化创建;

添加元素容量不足时
。当数组容量不足时,调用grow()方法进行扩容,每次扩容后容量都会变为原来的1.5倍(在原有容量基础上,扩容.5倍);
。扩容后,数组的最大容量不会超过 Integer,MAX_VALUE;

6.HashSet、LinkedHashSet 和 TreeSet 区别?

它们都是 Set 接口的实现类,区别主要包括:
Hashset:基于HashMap实现,元素唯一,无序;
LinkedHashSet:HashSet的子类,基于LinkedHashMap实现,元素唯一且有序,同时保持了哈希表的性能;,

Treeset :基于TreeMap实现,元素唯一,自动排序或按照自定义规则排序;

7.HashSet如何过滤重复元素?

Hashset内部使用一个HashMap作为数据结构,保存元素时,会使用这个HashMap的key 来进行保存。key是唯一的,所以重复元素会自动过滤。 

 8. HashMap 和 Hashtable 的区别?

HashMap Hashtable 都是 Map 接口的键值对集合实现类,它们的区别主要包括:

线程安全:HashMap 是非线程安全的,而HashTable线程安全;
执行效率:由于HashTable 使用synchronized同步锁实现线程安全,所以HashTable 效率要比                         HashMap 略低;·

使用 Nul1 做 key 和 value:
HashMap : 可以使用 nul1 作为 key 和value;
HashTable :不允许有nul1 键和 nu11值,否则会抛出NullPointerException 异常;
数据结构:
HashMap : 数组+链表+红黑树;
HashTable :数组+链表;
扩容方式:
HashMap :默认的初始化大小为16。之后每次扩充,容量变为原来的2倍;
HashTable: 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1;

9.HashMap的数据结构是什么?

HashMap的数据结构采用数组(哈希表)+链表+红黑树
1.数组(哈希表):
HashMap 内部定义了一个数组,数组中的每个位置被称为“桶”( Bucket),这是 HashMap 的基础结构[哈希表];。

下标位置:当添加一个新的key-value键值对时,会根据key的 hashcode(),通过哈希函数计算出一个新的哈希值h ash,并通过这个hash 值,计算key-value键值对在数组中的下标位置(桶Bucket);

数组容量:在添加第一个key-value键值对时,数组容量被初始化为16,并且可以根据key-value键值对的数量和负载因子,数组会自动按照2倍进行扩容;
2.链表:
数组的每个位置(Bucket桶)可以保存一个或多个key-value键值对;
当两个或更多的 key-value键值对,被映射保存到数组的同一个位置(桶Bucket)时,就产生了哈希表冲突;。

HashMap 使用链地址法,解决哈希冲突,这些键值对将以链表的形式存储在产生冲突的位置(桶Bucket);
3.红黑树
为了优化链表的查询性能,当链表长度超过一个阈值(默认是8)并且数组的容量大于等于64时,链表会转换成红黑树;
红黑树是一种自平衡的二叉查找树,它可以基于二分查找的方式,进行元素的查找,提高查找搜索性能,这对于较长的链表来说是一个明显的性能提升;
当红黑树中的节点数量减少到6个或更少时,红黑树将转换回链表;

10.HashMap中put( )的执行过程?

1.计算哈希码:
向HashMap 中添加一个新的key-value键值对时,首先会调用 key键的hashcode() 方法来计算一个hash 哈希值。(这个哈希值将被用于计算key-value键值对,在数组中保存时的下标位置)
2.计算数组下标:
计算完盼希值后会通过数组长度计算完 hash 哈希值后,HashMap会通过数组长度n,按照(n-1) & hash的方式,将哈希值转化为数组下标;按照的方式将哈希值转化为数组下标:
(n-1) & hash 的作用等同于 hash % n ,位运算比算术运算的效率高;
3.处理哈希冲突:
。如果不同 key按照各自不同的 hash 哈希值,计算的数组下标位置相同,这种情况称为哈希表冲突;
。HashMap 使用链地址法来处理哈希表冲突,在数组的每个位置存放一个链表或红黑树(当链表长度达到一定阈值时会转换为红黑树)。因此,新的键值对会被添加到当前位置的链表或红黑树中;
4.检查键是否已存在:
。在将键值对放入对应位置之前,HashMap会检查该位置是否存在与新键相等的对象;
。会调用equals()方法来比较键之间的相等性。如果找到相同的键,那么旧的值将被新值替换,返回旧值;否则,新键值对将被添加;
5.插入新节点:
.如果没有找到相等的键,新键值对将被插入到该位置的链表头部或红黑树中;
。当链表长度达到8且数组容量大于64时,链表会转化为红黑树,用来优化查找性能;

6.检查容量和扩容:
。每次插入新键值对后,HashMap会检查当前元素数量是否超过了它的扩容阈值(容量乘以       加载因子(默认为e.75));。如果超过了扩容阈值,HashMap会按照2倍进行扩容;
7.返回旧值:
。如果在插入过程中发现键已经存在,put()方法会返回旧的值;。如果键不存在,插入了一个新键值对,put()方法将返回 null;

11.HashMap如何计算key-value键值对元素在数组中的存储位置?

        为了使key-value元素可以均匀散列的保存在HashMap 的数组中,所以使用key的哈希值进行哈希扰动计算出一个新的 has h值。
        在JDK 1.8 版本以前,HashMap会将这个hash值与数组长度进行% 模运算出一个下标值,从而确定key-value在数组中的存储位置;
例如:数组长度默认为16,12580 % 16 =4,753951 % 16=15.所以哈希值为12580753951的键值对,会存储在数组下标为4和3的位置。
        在JDK 1.8 版本以后,由于“%”模运算性能消耗比较大,所以采用(长度-1)& hash的位运算方式来计算存储位置;
.例如:数组长度默认为16,(16- 1) & 125804,(16- 1)& 753951,所以哈希值为12580753951的键值对,会继续存储在数组下标为4和3的位置。

12.什么是哈希冲突?

。哈希冲突是在使用哈希函数或哈希表时遇到的一种特殊情况;
。如果发生在哈希函数,哈希冲突是指当发生在两个不同的输入值,经过哈希函数处理后产生了相同的哈希值:
。如果发生在哈希表,哈希冲突是由于哈希表的大小是有限的,而输入的数据集合可能是无限的,因此,当不同的输入数据映射到哈希表的同一个位置时,就会出现哈希冲突:

13.如何解决哈希冲突?

处理哈希冲突的方法主要有以下几种:
1.开放寻址法( open Addressing):当哈希冲突发生时,使用某种探测技术在哈希表中寻找下一个空位来存储数据。
2.链地址法( chaining):每个哈希表的槽位都维护一个链表。当哈希冲突发生时,所有哈希值相同的元素都被存储在同一个位置对应的链表中。这种方法也称为拉链法。
3.再哈希法( Rehashing):当哈希冲突发生时,使用第二个哈希函数再次计算哈希值,直到找到空槽位。这种方法会增加计算复
杂度,但可以减少哈希表的装载因子,从而提高查找效率。
4.建立一个公共溢出区:将哈希表分为基本表和溢出表两部分。所有哈希地址不冲突的元素都存放在基本表中,所有哈希地址冲突的元素都存放在溢出表中。

 14.HashMap如何解决哈希冲突?

HashMap使用蚰地址法来处理哈希表冲突,当产生哈陪冲突时,Hashap会将产生冲突的Key-Value被值时,通过一个链表保存在数组产生冲突的位置中;

15. HashMap 的长度为什么是2的幂次方(2的倍数)?

HanhMap为什么按照2他进行扩者?因为必须保持 HanhMap的的容量为2的哪次方
降低哈希冲突概率:
当Hashtap中的数组长度为2的幕次方,不同的key计算得到 index相同的几率较小,不容易产生冲突;.

提高计算效率:
。Hashtap中使用哈限表保存时,索引计算公式为1=(n-1)&hash.
。如果n为的幂次方,那么m-1的低位全是1,那么使用hash哈利值进行与操作时,可以保证低位的值不变的情况下,高位更多的参与运算,从而保证分布均匀散列;
。同时,(n-1)hash效果等同于hash xn,但是与运算属于位运算,效率比“%°模运算性能要高;

16.HashMap 影响性能的两个参数?

。构建Hashmap实例时有两个重要的参数会影响其性能:初始容量和加载因子
初始容量:用来规定暗希表数组的容量长度,默认为16,因为16是z的每次方的原国,所以在小数据量的情况下,能减少哈希冲突,提高性能,如果存储大寄量数据的时候,最好预先判断数据量,按照2的幕次方,提前预设初始寄量;
加载因子:用来表示暗希表中元素的填满程度,默认为0.75,越大则表示允许填满的元素就越多,哈希表的空间利用率就越壶,但是冲突的机会增加,反之,越小则冲突的机会就会越少,但是空间很多就浪费,
。所以,在设置初输容量时,应该考虑到初始容量及其加载因子,预估设置初输容量,最大限度降低广容操作频率,

17.HashMap的扩容机制?

:HanhMap的用来进行扩者的方法是renize[)方法::HanhHap在以下三种场景下,金触发扩容机制:
1   .当 HashMap通过无参构造方法创建,在第一次调用put(]方法添加key-value键值对时,数        组初始化为16;
2   .当HashMap中的元素个数超过扩容阈值threshold时,数组的容量按用原容量的2他进行        扩容:

。扩容阈值threshold数组容量x加歌因子LoadFacter
加载因子LoadFactor的默认值为0.75,歌组容量默认为16,扩容阈值threshold默认为12(16x0.7 s-12):
.所以,当HanhMap 中元素个数超过12 时,数组的容量按照原容量的1他进行扩容(16x2=32):。3.加入元素时,如果链表长度大于问值《默认为a)并且数组长度小于64,余产生政电扩容

18.HashMap为什么使用链表?

HashMap使用哈用表作为基础数据结构,当两个不同的key-value键值对,通过 hash哈希值计算数组下标,出现相网下标情况时,产生哈解冲突;
HashMap 使用“值地址法”解决哈陪冲突,所以离要使用链表,来保存产生哈陪冲突的key-valse细值对;

19.HashMap为什么使用红黑树?

Hashtap中的销表长度增长到一定长度,金导致搜索性能下降;(销表是线性方式搜索)
.所以,Hashtap会在链表过长时,将链表转换为红黑树,通过红黑树提供搜索性能,(红黑树是二分直找方式搜索)
Hashtap不直接用红黑树的原因是:链表油单易于维护,红黑树维护复杂、所以首选使用销表,只有销表长度过长时,才会转换成红黑树来提高搜索直找的性能:

20.红黑树有哪些特点?

HashHap中的红黑树
红南树型一种自平衡二叉查找树:
二叉查找树:树中的所有节点满型排序规则(left(root(right),可以基于二分直找进行搜索;
自平衡:保持左右子树的平衡,保持接索性能,不会出现退化成链表的根端情况(所有子节点都保存在左子树或右子树);,

。所以,HashMap使用红黑树来优化长链表;

21.HashMap链表转换红黑树的条件?

在HashMap中,当链表转换为红黑树,需要同时满足两个条件:链表长度大于等于3,井圆数组容量大于等于64:
1.链表长度:当链表的长度大于等于时(有个或服多的元素在Mashmep的同一个桶[bucket)中形成链表),这是因为
在链表长度较小的情况下,链表的线性查找效率还可以接受,但是一旦当链表长度较长时,必须通过将链表转模成红黑树这种方式.才能够提供更好的查找性能。
2.数组容量:在链表达到调值3时,同时还要检查Hashmap的底层数(哈希表》的容量大于等于64,因为Hashnap不会轻易使用红黑树,虽然红黑树的接索性能比链表好,但显维护重复杂,只有在链表显够长,只有红黑树才能带来明显性能提升时,才会进行特换。
。所以,如果数组容量小于4,即使链表长度超过日,也不会立即进行红黑刷的转换,而是先进行歃哨的扩容,将HanhMa P中的所有元素重新散列保存,用来降低链表的长度。《优先使用链表]

22.HashMap红黑树退化成链表的条件?

当红黑树中的节点数量减少到6个或更少时,红黑树将转换回链表。因为,红黑树中的节点过少时,使用栖表来保存,维护会更简单高效:。这种机制,可以让HanhMap在性能和资源使用之间找到一个平衡点;

23.HashMap的遍历方式?

▼使用entrySet()和for-each循环

 for (Map.Entry<String, Integer> entry : map.entryset()) {
System.out, println("Key =*+ entry.getkey() + ", Value = "+ entry.getvalue());    

}



使用keySet() 和values()
for (string key : map.keySet()){
System.out, println("Key =" + key);

}
for (Integer value : mmp.values()){
system,out,printin("Value =" + value);

}



使用Java 8的 forEach0方法
map,foreach((key, value) -> System,out,printin("Key = " + key + -.Vmlue="+ value));



使用entrySeD.stream0
map.entryset().stream()    
        forEach(entry -> $yatan,out,println("Key = " * entry-gatkey() + ", Value = " * entry. getvalue()));

24.HashMap、LinkedHashMap、TreeMap的区别?

HashMap:无序,基于数组+链表+红黑树实现; Linkedrashrag:有序,Hashap的子类;
.TreeHap:自动排序,按照key或者自定义 camparater比较器,进行排序;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值