《2021最新Java面试题全集-2021年第二版》不断更新完善!

    

第二章 集合容器

1:Java 集合容器都有哪些?

 

2:ListSetMap是否继承自Collection接口?

ListSet 是,Map 不是。

Map是键值对映射容器,与ListSet有明显的区别,而Set存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List是线性结构的容器,适用于按数值索引访问元素的情形。

 

3:Collection Collections 有什么区别?

·       Java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接子接口有ListSet

·       Collections是集合类的一个工具类,提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。

 

4:ListSetMap 之间的区别是什么?

List:包含的元素是有序可重复的

Set:包含的元素是无序不可重复的

Map:包含的元素key-valuekey不可重复

List以特定索引来存取元素,可以有重复元素。Set不能存放重复元素(用对象的equals()方法来区分元素是否重复)。Map保存键值对(key-value pair)映射,映射关系可以是一对一或多对一。

SetMap容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理论存取时间复杂度为O(1),而基于排序树版本的实现在插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果。

 

5:为什么集合类没有实现CloneableSerializable接口?

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

 

6:你了解大O符号(big-O notation)么?

O符号描述了当数据结构里面的元素增加的时候,算法的规模或者是一个渐进上界

O符号也可用来描述其他的行为,比如:内存消耗。因为集合类实际上是数据结构,我们一般使用大O符号基于时间,内存和性能来选择最好的实现。大O符号可以对大量数据的性能给出一个很好的说明。

7:HashMap Hashtable 有什么区别?

·       hashMap去掉了HashTable contains方法,但是加上了containsValue()和containsKey()方法。

·       hashTable是线程安全的,而HashMap是非线程安全的,效率上HashMaphashTable要高。

·       hashMap允许空键值,而hashTable不允许。

 

8:如何决定使用 HashMap 还是 TreeMap

对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。

简单点说,需要元素排序,选用TreeMap,否则选用HashMap

 

9:TreeMapTreeSet在排序时如何比较元素?

TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小。

TreeMap要求存放的键值对映射的键必须实现Comparable接口从而根据键对元素进行排序。

 

10:Collections工具类中的sort()方法如何比较元素?

Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较。

第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java中对函数式编程的支持)

 

11:说一下 HashMap 的实现原理?

HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 

HashMap的数据结构: Java编程语言中,最基本的结构就是两种,一个是数组,另外一个是引用。HashMap实际上是一个链表散列的数据结构,即数组和链表的结合体。

当我们往Hashmapput元素时,首先根据keyhashcode重新计算hash,根据hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放入链尾.如果数组中该位置没有元素,就直接将该元素放到数组的该位置上。

需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)O(logn)

 

12:说一下 HashSet 的实现原理?

·       HashSet底层由HashMap实现

·       HashSet的值存放于HashMapkey

·       HashMapvalue统一为PRESENT

 

13:ArrayList LinkedList 的区别是什么?

ArrayListLinkedList都实现了List接口,他们有以下的不同点:

·       ArrayList基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。

·       LinkedList底层数据结构是双向链表,不支持随机访问据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)

·       相对于ArrayListLinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。

·       LinkedListArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。

 

14:如何实现数组和 List 之间的转换?

·       List转换成为数组:调用ArrayListtoArray方法。

·       数组转换成为List:调用ArraysasList方法。

 

15:ArrayListVectorLinkedList的存储性能和特性

ArrayList Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢.

Vector中的方法由于添加了synchronized修饰,因此Vector是线程安全的容器,但性能上较ArrayList差,因此已经是Java中的遗留容器。

LinkedList使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。

Vector属于遗留容器(Java早期的版本中提供的容器,除此之外,HashtableDictionaryBitSetStackProperties都是遗留容器),已经不推荐使用.

但是由于ArrayListLinkedListed都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装饰模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)。

16:ArrayList Vector 的区别是什么?

·       Vector是同步的,是线程安全的,而ArrayList不是。如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList 

·       ArrayListVector运行速度快。 

·       ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。

 

17:Array ArrayList 有何区别?

·       Array可以容纳基本类型和对象,而ArrayList只能容纳对象。 

·       Array是指定大小后不可变的,而ArrayList大小是可变的。 

·       Array没有提供ArrayList那么多功能,比如addAllremoveAlliterator

·       对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。

 

18:ComparableComparator接口是干什么的?

Java提供了只包含一个compareTo()方法的Comparable接口。这个方法可以个给两个对象排序。具体来说,它返回负数,0,正数来表明已经存在的对象小于,等于,大于输入对象。

Java提供了包含compare()equals()两个方法的Comparator接口。compare()方法用来给两个输入参数排序,返回负数,0,正数表明第一个参数是小于,等于,大于第二个参数。equals()方法需要一个对象作为参数,它用来决定输入参数是否和comparator相等。只有当输入参数也是一个comparator并且输入参数和当前comparator的排序结果是相同的时候,这个方法才返回true

Comparable & Comparator 都是用来实现集合中元素的比较、排序的,只是 Comparable是在集合内部定义的方法实现的排序Comparator 是在集合外部实现的排序,所以,如想实现排序,就需要在集合外定义 Comparator 接口的方法或在集合内实现 Comparable 接口的方法。

 Comparator位于包java.util下,而Comparable位于包 java.lang Comparable 是一个对象本身就已经支持自比较所需要实现的接口(如 StringInteger 自己就可以完成比较大小操作,已经实现了Comparable接口) 自定义的类要在加入list容器中后能够排序,可以实现Comparable接口,在用Collections类的sort方法排序时,如果不指定Comparator,那么就以自然顺序排序, 这里的自然顺序就是实现Comparable接口设定的排序方式。

Comparator 是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。 可以说一个是自已完成比较,一个是外部程序实现比较的差别而已。

  Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。 比如:你想对整数采用绝对值大小来排序,Integer 是不符合要求的,你不需要去修改 Integer 类(实际上你也不能这么做)去改变它的排序行为,只要使用一个实现了 Comparator 接口的对象来实现控制它的排序就行了。

 

19:什么是Java优先级队列(Priority Queue)

PriorityQueue是一个基于优先级堆的无界队列,它的元素是按照自然顺序(natural order)排序的。

在创建的时候,我们可以给它提供一个负责给元素排序的比较器。PriorityQueue不允许null值,因为他们没有自然顺序,或者说他们没有任何的相关联的比较器。最后,PriorityQueue不是线程安全的,入队和出队的时间复杂度是O(log(n))

 

20:HashSetTreeSet有什么区别?

HashSet是由一个hash表来实现的,因此,它的元素是无序的。add()remove()contains()方法的时间复杂度是O(1)

TreeSet是由一个树形的结构来实现的,它里面的元素是有序的。因此,add()remove()contains()方法的时间复杂度是O(logn)

 

21: Queue poll() remove()有什么区别?

poll() remove() 都是从队列头取出一个元素,并删除队列中该元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常

 

22:哪些集合类是线程安全的?

·       vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。

·       statck:堆栈类,先进后出。

·       hashtable:就比hashmap多了个线程安全。

·       enumeration:枚举,相当于迭代器。

 

23:迭代器 Iterator 是什么?

迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为轻量级对象,因为创建它的代价小

 

24:Iterator 怎么使用?有什么特点?

Java中的Iterator功能比较简单,并且只能单向移动:

(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iteratornext()方法时,它返回序列的第一个元素。注意:iterator()方法是Java.lang.Iterable接口,Collection继承。

(2) 使用next()获得序列中的下一个元素。

(3) 使用hasNext()检查序列中是否还有元素。

(4) 使用remove()将迭代器新返回的元素删除。 

IteratorJava迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。

 

25:Enumeration接口和Iterator接口的区别有哪些?

Enumeration速度是Iterator2倍,同时占用更少的内存。但是,Iterator远远比Enumeration安全,因为其他线程不能够修改正在被iterator遍历的集合里面的对象。

同时,Iterator允许调用者删除底层集合里面的元素,这对Enumeration来说是不可能的。

 

26:Iterator ListIterator 有什么区别?

·       Iterator可用来遍历SetList集合,但是ListIterator只能用来遍历List 

·       Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。 

·       ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。