【Java面试知识点】Java集合框架

【Java面试知识点】Java集合框架

对集合的理解

(Java集合类框架的基本接口有哪些?)
集合类接口指定了一组叫做元素的对象。集合类接口的每一种具体的实现类都可以选择以它自己的方式对元素进行保存和排序。有的集合类允许重复的键,有些不允许。
Java集合类提供了一套设计良好的支持对一组对象进行操作的接口和类。Java集合类里面最基本的接口有:
Collection:代表一组对象,每一个对象都是它的子元素。
List:有顺序的collection,并且可以包含重复元素。
Set:不包含重复元素的Collection。
Queue:队列,可以实现先进进出。
Map:可以把键(key)映射到值(value)的对象,键不能重复。


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

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


什么是迭代器(Iterator)?

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


Iterator详解

Iterator接口提供遍历任何Collection的接口。
我们可以从一个Collection中使用迭代器方法来获取迭代器实例。
迭代器取代了Java集合框架中的Enumeration。
迭代器允许调用者在迭代过程中移除元素。

它包含有三个方法:
hasNext
next
remove


Iterator和ListIterator的区别是什么?

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


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

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


通过迭代器fail-fast属性,你明白了什么?

每次我们尝试获取下一个元素的时候,Iterator fail-fast属性检查当前集合结构里的任何改动。如果发现任何改动,它抛出ConcurrentModificationException。

Collection中所有Iterator的实现都是按fail-fast来设计的(ConcurrentHashMap和CopyOnWriteArrayList这类并发集合类除外)。


在迭代一个集合的时候,如何避免ConcurrentModificationException?

在遍历一个集合的时候,我们可以使用并发集合类来避免ConcurrentModificationException,比如使用CopyOnWriteArrayList,而不是ArrayList。


为何Iterator接口没有具体的实现?

Iterator接口定义了遍历集合的方法,但它的实现则是集合实现类的责任。每个能够返回用于遍历的Iterator的集合类都有它自己的Iterator实现内部类。

这就允许集合类去选择迭代器是fail-fast还是fail-safe的。比如,ArrayList迭代器是fail-fast的,而CopyOnWriteArrayList迭代器是fail-safe的。


解释HashMap的底层实现原理?【2017阿里电话面试真题】

Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数,它使用hashCode()和equals()方法来向集合/从集合添加和检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上。如果key已经存在了,value会被更新成新值。HashMap的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)。
更为详细的内容请参考HashMap工作原理及实现的总结


对ConcurrentHashMap的理解


ConCurrentHashMap中的Segment是不是越多越好?为什么?


HashSet和TreeSet有什么区别?

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

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


HashMap和TreeMap的区别

HashSet和TreeSet有什么区别 答案相同。


HashMap和Hashtable有什么区别?

HashMap和Hashtable都实现了Map接口,因此很多特性非常相似。
但是,他们有以下不同点:
(1)HashMap允许键和值是null,而Hashtable不允许键或者值是null。
(2)HashTable是同步的,而HashMap不是。所以HashMap适合单线程环境,HashTable适合多线程环境。
(3)在Java1.4中引入了LinkedHashMap,HashMap的一个子类,假如你想要遍历顺序,你很容易从HashMap转向LinkedHashMap,但是HashTable不是这样的,它的顺序是不可预知的。
(4)HashMap提供对key的Set进行遍历,因此它是fail-fast的,但HashTable提供对key的Enumeration进行遍历,它不支持fail-fast。
(5)HashTable被认为是个遗留的类,如果你寻求在迭代的时候修改Map,你应该使用CocurrentHashMap。

需要更加深入的理解和比较两者的异同。


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

下面列出了Array和ArrayList的不同点:

  • Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
  • Array大小是固定的,ArrayList的大小是动态变化的。
  • ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
  • 对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
  • 如果你要使用多维数组,使用[][]比List>更容易。

要清楚ArrayList的实现细节


ArrayList和Vector有何异同点?

ArrayList和Vector在很多时候都很类似。
(1)两者都是基于索引的,内部由一个数组支持。
(2)两者维护插入的顺序,我们可以根据插入顺序来获取元素。
(3)ArrayList和Vector的迭代器实现都是fail-fast的。
(4)ArrayList和Vector两者允许null值,也可以使用索引值对元素进行随机访问。

以下是ArrayList和Vector的不同点。
(1)Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。
(2)ArrayList比Vector快,它因为有同步,不会过载。
(3)ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。


如何权衡是使用无序的数组还是有序的数组

有序数组最大的好处在于查找的时间复杂度是O(log n),而无序数组是O(n)。

有序数组的缺点是插入操作的时间复杂度是O(n),因为值大的元素需要往后移动来给新元素腾位置。相反,无序数组的插入时间复杂度是常量O(1)。


在ArrayList和LinkedList的末尾添加元素,哪个更快?【2017阿里电话面试题目】

(自己的见解,如有问题,还望大家指正。)
LinkedList操作会更快。
ArrayList会根据数组的首地址和长度计算新元素的位置,然后将元素存储到计算出的位置上。
LinkedList包含有last指针,直接将last.next执行新元素,然后将新元素赋值给last即可。
当然两者都需要更新长度。


ArrayList与LinkedList的区别?

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

(1)ArrayList是由Array所支持的基于一个索引的数据结构,所以它提供对元素的随机访问,复杂度为O(1),但LinkedList存储一系列的节点数据,每个节点都与前一个和下一个节点相连接。所以,尽管有使用索引获取元素的方法,内部实现是从起始点开始遍历,遍历到索引的节点然后返回元素,时间复杂度为O(n),比ArrayList要慢。

(2)与ArrayList相比,在LinkedList中插入、添加和删除一个元素会更快,因为在一个元素被插入到中间的时候,不会涉及改变数组的大小,或更新索引。

(3)LinkedList比ArrayList消耗更多的内存,因为LinkedList中的每个节点存储了前后节点的引用。


哪些集合类提供对元素的随机访问?

ArrayList、HashMap、TreeMap和HashTable类提供对元素的随机访问。


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

PriorityQueue是基于堆排序算法实现的队列。

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


Stack和ArrayList的区别


Java集合中哪些是线程安全的?

并发集合类是什么?

Java1.5并发包(java.util.concurrent)包含线程安全集合类,允许在迭代时修改集合。迭代器被设计为safe-fast的,会抛出ConcurrentModificationException。
一部分类为:
CopyOnWriteArrayList、
CopyOnWriteArraySet,
ConcurrentHashMap


BlockingQueue是什么?

Java.util.concurrent.BlockingQueue是一个队列,
在进行检索或移除一个元素的时候,它会等待队列变为非空;
当在添加一个元素时,它会等待队列中的可用空间。

BlockingQueue接口是Java集合框架的一部分,主要用于实现生产者-消费者模式。
我们不需要担心等待生产者有可用的空间,或消费者有可用的对象,因为它都在BlockingQueue的实现类中被处理了。
Java提供了集中BlockingQueue的实现,比如
ArrayBlockingQueue、
LinkedBlockingQueue、
PriorityBlockingQueue,、
SynchronousQueue等。


对equals和hashCode方法的理解

(hashCode()和equals()方法的重要性体现在什么地方?)
Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引,当根据键获取值的时候也会用到这两个方法。如果没有正确的实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。而且,这两个方法也用来发现重复元素。所以这两个方法的实现对HashMap的精确性和正确性是至关重要的。

hashCode和equals这两个方法都是Object里面的方法。
hashCode的默认结果是System.identityHashCode(obj)的返回值,可以理解 obj 在内存中独一无二的地址,是对象的唯一标识。当使用 == 比较两个对象是否相等(应该是相同,是严格的相等,它们是否是同一个对象)时,比较的就是对象的独一无二的地址。

hashCode()和equals()方法有何重要性?

HashMap使用Key对象的hashCode()和equals()方法去决定key-value对的索引。当我们试着从HashMap中获取值的时候,这些方法也会被用到。如果这些方法没有被正确地实现,在这种情况下,两个不同Key也许会产生相同的hashCode()和equals()输出,HashMap将会认为它们是相同的,然后覆盖它们,而非把它们存储到不同的地方。同样的,所有不允许存储重复数据的集合类都使用hashCode()和equals()去查找重复,所以正确实现它们非常重要。

equals()和hashCode()的实现应该遵循以下规则:
(1)如果o1.equals(o2),那么o1.hashCode() == o2.hashCode()总是为true的。
(2)如果o1.hashCode() == o2.hashCode(),并不意味着o1.equals(o2)会为true。


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

java.lang.Comparable 和 java.util.Comparator 接口(注意,它们的泛型类型参数会传递给对应的比较函数)

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

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


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

根据应用的需要正确选择要使用的集合的类型对性能非常重要,比如:假如元素的数量是固定的,而且能事先知道,我们就应该用Array而不是ArrayList。

有些集合类允许指定初始容量。因此,如果我们能估计出存储的元素的数目,我们可以设置初始容量来避免重新计算hash值或者是扩容。

为了类型安全,可读性和健壮性的原因总是要使用泛型。同时,使用泛型还可以避免运行时的ClassCastException。

使用JDK提供的不变类(immutable class)作为Map的键可以避免为我们自己的类实现hashCode()和equals()方法。

编程的时候接口优于实现。
底层的集合实际上是空的情况下,返回长度是0的集合或者是数组,不要返回null。


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

Enumeration的速度是Iterator的两倍,也使用更少的内存。
Enumeration是非常基础的,也满足了基础的需要。
但是,与Enumeration相比,Iterator更加安全,因为当一个集合正在被遍历的时候,它会阻止其它线程去修改集合。

迭代器取代了Java集合框架中的Enumeration。迭代器允许调用者从集合中移除元素,而Enumeration不能做到。
为了使它的功能更加清晰,迭代器方法名已经经过改善。


a.hashCode() 有什么用?与 a.equals(b) 有什么关系

hashCode() 方法是相应对象整型的 hash 值。它常用于基于 hash 的集合类,如 Hashtable、HashMap、LinkedHashMap等等。它与 equals() 方法关系特别紧密。

根据 Java 规范,两个使用 equal() 方法来判断相等的对象,必须具有相同的 hash code。


poll() 方法和 remove() 方法的区别?

poll() 和 remove() 都是从队列中取出一个元素,
poll() 在获取元素失败的时候会返回空,
remove() 失败的时候会抛出异常。


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

(1)根据需要选择正确的集合类型。比如,如果指定了大小,我们会选用Array而非ArrayList。如果我们想根据插入顺序遍历一个Map,我们需要使用TreeMap。如果我们不想重复,我们应该使用Set。

(2)一些集合类允许指定初始容量,所以如果我们能够估计到存储元素的数量,我们可以使用它,就避免了重新哈希或大小调整。

(3)基于接口编程,而非基于实现编程,它允许我们后来轻易地改变实现。(编程的时候接口优于实现。)

(4)总是使用类型安全的泛型,避免在运行时出现ClassCastException。

(5)使用JDK提供的不可变类作为Map的key,可以避免自己实现hashCode()和equals()。

(6)尽可能使用Collections工具类,或者获取只读、同步或空的集合,而非编写自己的实现。它将会提供代码重用性,它有着更好的稳定性和可维护性。

(7) 底层的集合实际上是空的情况下,返回长度是0的集合或者是数组,不要返回null。


用哪两种方式来实现集合的排序

你可以使用有序集合,如 TreeSet 或 TreeMap,
你也可以使用有顺序的的集合,如 list,然后通过 Collections.sort() 来排序。


在遍历 ArrayList 时移除一个元素

该问题的关键在于面试者使用的是 ArrayList 的 remove() 还是 Iterator 的 remove()方法。
使用Iterator的remove方法来实现在遍历的过程中移除元素,它不会出现 ConcurrentModificationException 异常。


我们能自己写一个容器类,然后使用 for-each 循环码

可以,你可以写一个自己的容器类。如果你想使用 Java 中增强的循环来遍历,你只需要实现 Iterable 接口。如果你实现 Collection 接口,默认就具有该属性。


ArrayList 和 HashMap 的默认大小是多数?(答案)

在 Java 7 中,ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂)。
这就是 Java 7 中 ArrayList 和 HashMap 类的代码片段:

// from ArrayList.java JDK 1.7
private static final int DEFAULT_CAPACITY = 10;

//from HashMap.java JDK 7
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

当容量不足时,各个容器会扩展多大呢?


我们如何对一组对象进行排序

如果我们需要对一个对象数组进行排序,我们可以使用Arrays.sort()方法。

如果我们需要排序一个对象列表,我们可以使用Collection.sort()方法。两个类都有用于自然排序(使用Comparable)或基于标准的排序(使用Comparator)的重载方法sort()。Collections内部使用数组排序方法,所有它们两者都有相同的性能,只是Collections需要花时间将列表转换为数组。

TreeSet和TreeMap。

PriorityQueue


如何从给定集合那里创建一个synchronized的集合

我们可以使用Collections.synchronizedCollection(Collection c)根据指定集合来获取一个synchronized(线程安全的)集合。


集合框架里实现的通用算法有哪些?

Java集合框架提供常用的算法实现,比如排序和搜索。Collections类包含这些方法实现。大部分算法是操作List的,但一部分对所有类型的集合都是可用的。部分算法有排序、搜索、混编、最大最小值。


参考:
最近5年133个Java面试问题列表
40个Java集合面试问题和答案

你可能感兴趣的:(学习笔记)