面试题----集合

本文详细介绍了Java集合框架中的List,Set,Map三大接口及其常用实现类,包括它们的底层数据结构、特点和用途。还讨论了迭代器的作用,线程安全问题,以及如何在多线程环境中选择合适的集合类。此外,对比了ArrayList和Array的区别,分析了HashMap的实现原理和扩容机制,并探讨了不同集合类的遍历方式和线程安全策略。
摘要由CSDN通过智能技术生成

目录

概述 

List,Set,Map

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

List

Set

Map

为什么要使⽤集合?

如何选⽤集合?

迭代器 Iterator 是什么?

迭代器 Iterator 有啥⽤?

 Iterator 和 ListIterator 的区别是什么?

 有哪些集合是线程不安全的?怎么解决呢?

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

Arraylist 和 Vector 的区别?

Arraylist 与 LinkedList 区别?

双向链表和双向循环链表

comparable 和 Comparator 的区别

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

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

⽆序性和不可重复性的含义是什么

HashSet 的底层实现是什么?  

⽐较 HashSet、LinkedHashSet 和 TreeSet 三者的异同

HashMap 和 Hashtable 的区别

HashMap 和 HashSet 区别

 HashMap 和 TreeMap 区别

HashSet 如何检查重复

HashMap 的底层实现

HashMap 的工作原理是什么? 

Hashmap 什么时候进行扩容呢?  

HashMap 的⻓度为什么是 2 的幂次⽅

HashMap 多线程操作导致死循环问题

HashMap 有哪⼏种常⻅的遍历⽅式?

 LinkedHashMap 的实现原理?

ConcurrentHashMap 和 Hashtable 的区别

 Collections ⼯具类

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

 Collection 和 Collections 的区别。

什么是快速失败(fail-fast)?

什么是安全失败(fail-safe)呢? 

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


概述 

从上图可以看出,在 Java 中除了以 Map 结尾的类之外, 其他类都实现了 Collection 接⼝。 并且,以 Map 结尾的类都实现了 Map 接⼝

List,Set,Map

  • List (对付顺序的好帮⼿): 存储的元素是有序的、可重复的。
  • Set (注重独⼀⽆⼆的性质): 存储的元素是⽆序的、不可重复的。
  • Map ( Key 来搜索的专家): 使⽤键值对(kye-value)存储,类似于数学上的函数 y=f(x)“x”代表 key"y"代表 valueKey 是⽆序的、不可重复的,value 是⽆序的、可重复的,每个键最多映射到⼀个值。

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

List

  • Arraylist Object[] 数组
  • Vector Object[] 数组
  • LinkedList : 双向链表(JDK1.6 之前为循环链表,JDK1.7 取消了循环)

Set

  • HashSet (⽆序,唯⼀): 基于 HashMap 实现的,底层采⽤ HashMap 来保存元素
  • LinkedHashSet LinkedHashSet HashSet 的⼦类,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的 LinkedHashMap 其内部是基于 HashMap 实现⼀样,不过还是有⼀点点区别的
  • TreeSet (有序,唯⼀): 红⿊树(⾃平衡的排序⼆叉树)

Map

  • HashMap JDK1.8 之前 HashMap 由数组+链表组成的,数组是 HashMap 的主体,链表则是主 要为了解决哈希冲突⽽存在的(拉链法解决冲突)。JDK1.8 以后在解决哈希冲突时有了较⼤ 的变化,当链表⻓度⼤于阈值(默认为 8)(将链表转换成红⿊树前会判断,如果当前数组的⻓ 度⼩于 64,那么会选择先进⾏数组扩容,⽽不是转换为红⿊树)时,将链表转化为红⿊树,以 减少搜索时间
  • LinkedHashMap LinkedHashMap 继承⾃ HashMap ,所以它的底层仍然是基于拉链式散 列结构即由数组和链表或红⿊树组成。另外, LinkedHashMap 在上⾯结构的基础上,增加了 ⼀条双向链表,使得上⾯的结构可以保持键值对的插⼊顺序。同时通过对链表进⾏相应的操作, 实现了访问顺序相关逻辑。
  • Hashtable : 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突 ⽽存在的
  • TreeMap : 红⿊树(⾃平衡的排序⼆叉树)

为什么要使⽤集合?

  • 当我们需要保存⼀组类型相同的数据的时候,我们应该是⽤⼀个容器来保存,这个容器就是数组,但是,使⽤数组存储对象具有⼀定的弊端, 因为我们在实际开发中,存储的数据的类型是多种多样的, 于是,就出现了集合,集合同样也是⽤来存储多个数据的。
  • 数组的缺点是⼀旦声明之后,⻓度就不可变了;同时,声明数组时的数据类型也决定了该数组存储的数据的类型;⽽且,数组存储的数据是有序的、可重复的,特点单⼀。 但是集合提⾼了数据存储的灵活 性,Java 集合不仅可以⽤来存储不同类型不同数量的对象,还可以保存具有映射关系的数据

如何选⽤集合?

  • 主要根据集合的特点来选⽤,⽐如我们需要根据键值获取到元素值时就选⽤ Map 接⼝下的集合,需 要排序时选择 TreeMap ,不需要排序时就选择 HashMap ,需要保证线程安全就选⽤ ConcurrentHashMap
  • 当我们只需要存放元素值时,就选择实现 Collection 接⼝的集合,需要保证元素唯⼀时选择实现 Set 接⼝的集合⽐如 TreeSet HashSet ,不需要就选择实现 List 接⼝的⽐如 ArrayList LinkedList ,然后再根据实现这些接⼝的集合的特点来选⽤。

迭代器 Iterator 是什么?

Iterator 对象称为迭代器(设计模式的⼀种),迭代器可以对集合进⾏遍历,但每⼀个集合内部的数据结构可能是不尽相同的,所以每⼀个集合存和取都很可能是不⼀样的,虽然我们可以⼈为地在每⼀个类中定义 hasNext() next() ⽅法,但这样做会让整个集合体系过于臃肿。于是就有了迭代 器。

迭代器 Iterator 有啥⽤?

Iterator 主要是⽤来遍历集合⽤的,它的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModificationException 异常,触发快速失败机制(fail-fast)。

 Iterator 和 ListIterator 的区别是什么?

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

 有哪些集合是线程不安全的?怎么解决呢?

我们常⽤的 Arraylist , LinkedList , Hashmap , HashSet , TreeSet , TreeMap PriorityQueue 都不是线程安全的。
解决办法很简单,可以使⽤线程安全的集合来代替。
如果你要使⽤线程安全的集合的话, java.util.concurrent 包中提供了很多并发容器供你使⽤:
  • 1. ConcurrentHashMap : 可以看作是线程安全的 HashMap
  • 2. CopyOnWriteArrayList :可以看作是线程安全的 ArrayList ,在读多写少的场合性能⾮常 好,远远好于 Vector .
  • 3. ConcurrentLinkedQueue :⾼效的并发队列,使⽤链表实现。可以看做⼀个线程安全的 LinkedList ,这是⼀个⾮阻塞队列。
  • 4. BlockingQueue : 这是⼀个接⼝,JDK 内部通过链表、数组等⽅式实现了这个接⼝。表示阻塞 队列,⾮常适合⽤于作为数据共享的通道。
  • 5. ConcurrentSkipListMap :跳表的实现。这是⼀个 Map ,使⽤跳表的数据结构进⾏快速查 找。

 

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

  • Array 可以包含基本类型和对象类型,ArrayList 只能包含对象类型。
  • Array 大小是固定的,ArrayList 的大小是动态变化的。
  • ArrayList 处理固定大小的基本数据类型的时候,这种方式相对比较慢。

Arraylist Vector 的区别?

  • ArrayList List 的主要实现类,底层使⽤ Object[ ](数组)存储,适⽤于频繁的查找⼯作,线程不 安全,需要储存空间时,扩容到原来1.5倍 ;
  • Vector 是 List 的古⽼实现类,底层使⽤ Object[ ](数组)存储,线程安全的,可以代替ArrayList做线程安全的集合,在读多写少的情况下可以使用JUC(java.util.concurrent)包下的ConcurrentArrayList,需要扩容时扩容到原来2倍,且可以设置增长的空间大小。

都是插入删除慢,查询快 

Arraylist LinkedList 区别?

  • 是否保证线程安全: ArrayList LinkedList 都是不同步的,也就是不保证线程安全;
  • 底层数据结构: Arraylist 底层使⽤的是 Object 数组LinkedList 底层使⽤的是 向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下⾯有介绍到!)
  • 插⼊和删除是否受元素位置的影响:ArrayList 采⽤数组存储,所以插⼊和删除元素的 时间复杂度受元素位置的影响。 ⽐如:执⾏ add(E e) ⽅法的时候, ArrayList 会默认在 将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插⼊和删除元素的话( add(int index, E element) )时间复杂度就为 O(n-i)。因为在进 ⾏上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执⾏向后位/向前移⼀位的 操作。LinkedList 采⽤链表存储,所以对于 add(E e) ⽅法的插⼊,删除元素时间复杂 度不受元素位置的影响,近似 O(1),如果是要在指定位置 i 插⼊和删除元素的话( (add(int index, E element) ) 时间复杂度近似为 o(n)) 因为需要先移动到指定位置再插⼊。
  • 是否⽀持快速随机访问: LinkedList 不⽀持⾼效的随机元素访问,⽽ ArrayList ⽀持。 快速随机访问就是通过元素的序号快速获取元素对象(对应于 get(int index) ⽅法)
  • 内存空间占⽤: ArrayList 的空 间浪费主要体现在在 list 列表的结尾会预留⼀定的容量空 间,⽽ LinkedList 的空间花费则体现在它的每⼀个元素都需要消耗⽐ ArrayList 更多的空间 (因为要存放直接后继和直接前驱以及数据)。

双向链表和双向循环链表

双向链表: 包含两个指针,⼀个 prev 指向前⼀个节点,⼀个 next 指向后⼀个节点
双向循环链表: 最后⼀个节点的 next 指向 head ,⽽ head prev 指向最后⼀个节点,构成⼀个环。

comparable Comparator 的区别

  • comparable 接⼝实际上是出⾃ java.lang 包 它有⼀个 compareTo(Object obj) ⽅法⽤ 来排序
  • comparator 接⼝实际上是出⾃ java.util 包它有⼀个 compare(Object obj1, Object obj2) ⽅法⽤来排序
⼀般我们需要对⼀个集合使⽤⾃定义排序时,我们就要重写 compareTo() ⽅法或 compare() ⽅法,当我们需要对某⼀个集合实现两种排序⽅式,⽐如⼀个 song 对象中的歌名和歌⼿名分别采⽤种排序⽅法的话,我们可以重写 compareTo() ⽅法和使⽤⾃制的 Comparator ⽅法或者以两个 Comparator 来实现歌名排序和歌星名排序,第⼆种代表我们只能使⽤两个参数版的Collections.sort() .

 

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

 equals()

equals() 和 == 方法决定引用值是否指向同一对象 equals() 在类中被覆盖,为的是
当两个分离的对象的内容和类型相配的话,返回真值。

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

对。
  • 如果对象要保存在 HashSet 或 HashMap 中,它们的 equals 相等,那么,它 们的 hashcode 值就必须相等。
  • 如果不是要保存在 HashSet 或 HashMap,则与 hashcode 没有什么关系了,这时 候 hashcode 不等是可以的

⽆序性和不可重复性的含义是什么

  • 什么是⽆序性?⽆序性不等于随机性 ,⽆序性是指存储的数据在底层数组中并⾮按照数组索引的顺序添加 ,⽽是根据数据的哈希值决定的。
  • 什么是不可重复性?不可重复性是指添加的元素按照 equals()判断时 ,返回 false,需要同时重 写 equals()⽅法和 HashCode()⽅法。

HashSet 的底层实现是什么?  

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

⽐较 HashSet、LinkedHashSet 和 TreeSet 三者的异同

  • HashSet Set 接⼝的主要实现类 ,HashSet 的底层是 HashMap,线程不安全的,可以存储 null 值;
  • LinkedHashSet HashSet 的⼦类,能够按照添加的顺序遍历;
  • TreeSet 底层使⽤红⿊树,能够按照添加元素的顺序进⾏遍历,排序的⽅式有⾃然排序和定制排序。

HashMap Hashtable 的区别

  • 线程是否安全: HashMap 是⾮线程安全的,HashTable 是线程安全的,因为 HashTable 内部的 ⽅法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使⽤ ConcurrentHashMap 吧!);
  • 效率: 因为线程安全的问题,HashMap 要⽐ HashTable 效率⾼⼀点。另外,HashTable 基本被淘汰,不要在代码中使⽤它;
  • Null key Null value 的⽀持: HashMap 可以存储 null key value,但 null 为键只能有⼀个,null 作为值可以有多个;HashTable 不允许有 null 键和 null 值,否则会 抛出 NullPointerException
  • 初始容量⼤⼩和每次扩充容量⼤⼩的不同 :创建时如果不指定容量初始值,Hashtable 认的初始⼤⼩为 11,之后每次扩充,容量变为原来的 2n+1HashMap 默认的初始化⼤⼩为 16。之后每次扩充,容量变为原来的 2 倍。创建时如果给定了容量初始值,那么 Hashtable 会直接使⽤你给定的⼤⼩,⽽ HashMap 会将其扩充为 2 的幂次⽅⼤⼩(HashMap 中的 tableSizeFor() ⽅法保证,下⾯给出了源代码)。也就是说 HashMap 总是使⽤ 2 的幂作为哈希表的⼤⼩,后⾯会介绍到为什么是 2 的幂次⽅。
  • 5. 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较⼤的变化,当链表⻓度⼤于 阈值(默认为 8)(将链表转换成红⿊树前会判断,如果当前数组的⻓度⼩于 64,那么会选择 先进⾏数组扩容,⽽不是转换为红⿊树)时,将链表转化为红⿊树,以减少搜索时间。Hashtable 没有这样的机制。

HashMap HashSet 区别

如果你看过 HashSet 源码的话就应该知道: HashSet 底层就是基于 HashMap 实现的。( HashSet 的 源码⾮常⾮常少,因为除了 clone() writeObject() readObject() HashSet ⾃⼰不得不实现之外,其他⽅法都是直接调⽤ HashMap 中的⽅法。

 HashMap TreeMap 区别

  • TreeMap HashMap 都继承⾃ AbstractMap ,但是需要注意的是 TreeMap 它还实现了
  • NavigableMap 接⼝和 SortedMap 接⼝。
  • 实现 NavigableMap 接⼝让 TreeMap 有了对集合内元素的搜索的能⼒。
  • 实现 SortMap 接⼝让 TreeMap 有了对集合中的元素根据键排序的能⼒。默认是按 key 的升序排序,不过我们也可以指定排序的⽐较器。
  • 相⽐于 HashMap 来说 TreeMap 主要多是对集合中的元素根据键排序的能⼒以及对集合内元素的搜索的能⼒。

HashSet 如何检查重复

当你把对象加⼊ HashSet 时, HashSet 会先计算对象的 hashcode 值来判断对象加⼊的位置,同时也会与其他加⼊的对象的 hashcode 值作⽐较,如果没有相符的 hashcode HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调⽤ equals() ⽅法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让加⼊操作成功。

HashMap 的底层实现

JDK1.8之前

JDK1.8 之前 HashMap 底层是 数组和链表 结合在⼀起使⽤也就是 链表散列 HashMap 通过 key 的hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这⾥的 n 指的是数组的⻓度),如果当前位置存在元素的话,就判断该元素与要存⼊的元素的 hash值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。
扰动函数:指的就是 HashMap hash ⽅法。使⽤ hash ⽅法也就是扰动函数是为了防⽌⼀些实现 ⽐较差的 hashCode() ⽅法 换句话说使⽤扰动函数之后可以减少碰撞。
拉链法 :将链表和数组相结合。也就是说创建⼀个链表数组,数组中每⼀格就是⼀个链 表。若遇到哈希冲突,则将冲突的值加到链表中即可。
JDK1.8 之后
JDK1.8 之后在解决哈希冲突时有了较⼤的变化,当链表⻓度⼤于阈值(默认为
8 )(将链表转换成红⿊树前会判断,如果当前数组的⻓度⼩于 64 ,那么会选择先进⾏数组扩容,⽽不 是转换为红⿊树)时,将链表转化为红⿊树,以减少搜索时间。

HashMap 的工作原理是什么? 

Java 中的 HashMap 是以键值对 (key-value) 的形式存储元素的。HashMap 需要
一个 hash 函数,它使用 hashCode()和 equals()方法来向集合 / 从集合添加和检
索元素。当调用 put() 方法的时候,HashMap 会计算 key 的 hash 值,然后把键
值对存储在集合中合适的索引上。 如果 key 已经存在了,value 会被更新成新值。

Hashmap 什么时候进行扩容呢?  

        当 hashmap 中的元素个数超过(负载因子*数组长度) loadFactor 时,就会进行数组扩容, loadFactor (负载因子)的默认值为 0.75,也就是说,默认情况下,数组大小为 16,那么当 hashmap 中元素个数超过 160.75=12 的时候,就把数组的大小扩展为 216=32, 即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操 作。
        所以如果我们已经预知 hashmap 中元素的个数,那么预设元素的个数能够有效
的提高 hashmap 的性能。比如说,我们有 1000 个元素 new HashMap(1000),
但是理论上来讲 new HashMap(1024) 更合适,不过上面 annegu 已经说过,即使
是 1000,hashmap 也自动会将其设置为 1024。 但是 new HashMap(1024) 还
不是更合适的,因为 0.75*1000 < 1000, 也就是说为了让 0.75 * size > 1000, 我们
必须这样 new HashMap(2048) 才最合适,既考虑了 & 的问题,也避免了 resize
的问题。

HashMap 的⻓度为什么是 2 的幂次⽅

Hash 值的范围值 -2147483648 2147483647 ,前后加起来⼤概 40 亿的映射空间,只要哈希函数 映射得⽐较均匀松散,⼀般应⽤是很难出现碰撞的。但问题是⼀个 40 亿⻓度的数组,内存是放不下 的。所以这个散列值是不能直接拿来⽤的。⽤之前还要先做对数组的⻓度取模运算,得到的余数才能⽤ 来要存放的位置也就是对应的数组下标。这个数组下标的计算⽅法是“ (n - 1) & hash 。( n 代表数组⻓度)这也就解释了 HashMap 的⻓度为什么是 2 的幂次⽅。
为了能让 HashMap 存取⾼效,尽量较少碰撞,也就是要尽量把数据分配均匀。
这个数组下标的计算⽅法是 (n - 1) & hash ”。 n 代表 数组⻓度)

为什么这样能均匀分布减少碰撞呢?2的n次方实际就是1后面n个0,2的n次方-1  实际就是n个1;
例如长度为9时候,3&(9-1)=0  2&(9-1)=0 ,都在0上,碰撞了;
例如长度为8时候,3&(8-1)=3  2&(8-1)=2 ,不同位置上,不碰撞;

HashMap 多线程操作导致死循环问题

主要原因在于并发下的 Rehash 会造成元素之间会形成⼀个循环链表。不过, jdk 1.8 后解决了这个问题,但是还是不建议在多线程下使⽤ HashMap, 因为多线程下使⽤ HashMap 还是会存在其他问题⽐如数 据丢失。并发环境下推荐使⽤ ConcurrentHashMap 。(java.util.concurrent包里提供的并发容器)

HashMap 有哪⼏种常⻅的遍历⽅式?

HashMap 遍历从大的方向来说,可分为以下 4 类

  • 迭代器(Iterator)方式遍历;

  • For Each 方式遍历;

  • lambda 表达式遍历(JDK 1.8+);

  • Streams API 遍历(JDK 1.8+)。

但每种类型下又有不同的实现方式,因此具体的遍历方式又可以分为以下 7 种:

  • 使用迭代器(Iterator)EntrySet 的方式进行遍历;
  • 使用迭代器(Iterator)KeySet 的方式进行遍历;
  • 使用 For Each EntrySet 的方式进行遍历;
  • 使用 For Each KeySet 的方式进行遍历;
  • 使用 Lambda 表达式的方式进行遍历;
  • 使用 Streams API 单线程的方式进行遍历;
  • 使用 Streams API 多线程的方式进行遍历。
     

 LinkedHashMap 的实现原理?

LinkedHashMap 也是基于 HashMap 实现的,不同的是它定义了一个 Entry header,这个 header 不是放在 Table 里,它是额外独立出来的。 LinkedHashMap 通过继承 hashMap 中的 Entry, 并添加两个属性 Entrybefore,after, 和 header 结合起来组成一个双向链表,来实现按插入顺序或访问顺序 排序。LinkedHashMap 定义了排序模式 accessOrder,该属性为 boolean 型变 量,对于访问顺序,为 true;对于插入顺序,则为 false。一般情况下,不必指定排 序模式,其迭代顺序即为默认为插入顺序

ConcurrentHashMap Hashtable 的区别

  • 底层数据结构: JDK1.7 ConcurrentHashMap 底层采⽤ 分段的数组+链表 实现,JDK1.8 ⽤的数据结构跟 HashMap1.8 的结构⼀样,数组+链表/红⿊⼆叉树。Hashtable JDK1.8 之前 HashMap 的底层数据结构类似都是采⽤ 数组+链表 的形式,数组是 HashMap 的主体,链表 则是主要为了解决哈希冲突⽽存在的;
  • 实现线程安全的⽅式(重要): JDK1.7 的时候,ConcurrentHashMap(分段锁) 对整个 桶数组进⾏了分割分段(Segment),每⼀把锁只锁容器其中⼀部分数据,多线程访问容器⾥不同 数据段的数据,就不会存在锁竞争,提⾼并发访问率。 到了 JDK1.8 的时候已经摒弃了 Segment 的概念,⽽是直接⽤ Node 数组+链表+红⿊树的数据结构来实现,并发控制使⽤ synchronized CAS 来操作。(JDK1.6 以后 对 synchronized 锁做了很多优化) 整个看起 来就像是优化过且线程安全的 HashMap,虽然在 JDK1.8 中还能看到 Segment 的数据结构,但 是已经简化了属性,只是为了兼容旧版本;Hashtable(同⼀把锁) :使⽤ synchronized 来保 证线程安全(全表锁),效率⾮常低下。当⼀个线程访问同步⽅法时,其他线程也访问同步⽅法,可能会进 ⼊阻塞或轮询状态,如使⽤ put 添加元素,另⼀个线程不能使⽤ put 添加元素,也不能使⽤get,竞争会越来越激烈效率越低。

 Collections ⼯具类

排序

  • reverse 反转
  • sort排序
  • swap交换元素位置

查找

  • max找最大

 同步控制

collections里提供了很多方法来实现线程安全,但不推荐用,尽量使用java.util.concurrent(JUC)包下的并发集合

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

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

 Collection 和 Collections 的区别。

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

什么是快速失败(fail-fast)?

快速失败 (fail-fast) Java 集合的⼀种错误检测机制 在使⽤迭代器对集合进⾏遍历的时候,我们 在多线程下操作⾮安全失败 (fail-safe) 的集合类可能就会触发 fail-fast 机制,导致抛出
ConcurrentModificationException 异常。 另外,在单线程下,如果在遍历过程中对集合对象
的内容进⾏了修改的话也会触发 fail-fast 机制。

什么是安全失败(fail-safe)呢? 

采⽤安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,⽽是先复制原有集合内容,在 拷⻉的集合上进⾏遍历。所以,在遍历过程中对原集合所作的修改并不能被迭代器检测到,故不会抛 ConcurrentModificationException 异常

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

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

### 回答1: 《2021测试面试题 - pdf》是一本面试题集合,以PDF格式呈现。这本书可能包含了各种不同类型的面试题,涵盖了不同领域和层级的职位。 阅读这本书可以提供一些关于面试准备的指导,帮助应聘者了解常见的面试问题,以及如何回答这些问题。它可以帮助应聘者熟悉面试过程,了解面试官的期望,并为他们的面试做好准备。 这本书可能包含常见的面试问题,例如个人背景介绍、职业发展规划、技术能力、解决问题的能力、团队合作等。通过阅读这些问题,应聘者可以思考如何回答,并准备一些典型的示例和故事来支持他们的回答。 这本书还可能提供一些面试技巧和建议,包括如何在面试中展示自己的能力和经验,如何有效地回答问题,如何展示个人的职业素养等。 总之,《2021测试面试题 - pdf》是一本面试题集合,旨在帮助应聘者准备面试,了解常见的面试问题,并提供一些面试技巧和建议。对于那些正在寻找工作或准备面试的人来说,这本书可能是一个有用的资源。 ### 回答2: 《2021测试面试题- pdf》是一本面试题集合,针对测试工程师岗位的招聘面试进行了整理和编写。这本题集包含了多种类型的测试题目,涵盖了软件测试的各个方面,旨在帮助招聘单位了解应聘者的测试知识和技能。 这本面试题集的内容包括但不限于测试基础知识、测试策略和方法、测试工具和框架、自动化测试、性能测试、安全测试等。这些题目有些是选择题,有些是简答题或编程题,都是经过精心设计的,可以帮助招聘单位全面地了解应聘者在测试领域的实际能力和经验。 对于应聘者而言,阅读和解答《2021测试面试题- pdf》可以帮助他们复习和巩固测试知识,并提前了解可能会在面试中遇到的问题和考察点。通过认真准备,应聘者可以在面试中更好地回答问题,展示自己的实际能力和经验,提高获得聘用的机会。 对于招聘单位而言,这本题集可以作为一个参考工具,用于筛选和评估应聘者。招聘单位可以根据《2021测试面试题- pdf》中的问题,结合自己的需求和要求,从中选取适合的问题进行面试,以衡量应聘者的能力和适应性。 综上所述,《2021测试面试题- pdf》是一本用于软件测试岗位招聘面试的题目集,旨在帮助招聘单位了解应聘者的测试知识和技能,同时也为应聘者提供了一个复习和准备面试的工具。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值