Java Collections框架是Java编程语言的核心API之一。java
这是Java面试问题的重要主题之一。在这里,我列出了一些重要的Java集合面试问题和解答,以帮助您进行面试。这直接来自我14年以上的Java编程经验。程序员
Java 8对 Collection API 进行了重大更改。一些更改是:面试
Iterator
接口中的forEachRemaining(Consumer action)
,MapreplaceAll()
,compute()
,merge()
方法。每一个编程语言都使用集合,和最初的Java版本包含几个集合类:Vector,Stack,Hashtable和Array。可是从较高的范围和用法来看,Java 1.2提出了Collections Framework,该框架将全部collections接口,实现和算法分组。算法
Java的集合经过使用泛型和并发集合类进行线程安全操做已经走了很长一段路。它还包括在Java的并发包中的阻塞接口及其实现。数据库
Collections 框架的一些好处是;编程
Java 1.5带有泛型,全部集合接口和实现都大量使用它。泛型容许咱们提供集合能够包含的Object的类型,所以,若是您尝试添加其余类型的任何元素,则引起编译时错误。数组
这样能够避免在运行时发生ClassCastException,由于您将在编译时收到错误。因为咱们不须要使用强制转换和_实例化_运算符,所以泛型也使代码更干净。缓存
Collection 表示集合层次结构的根。Collection表示一组元素的对象。Java平台不提供此接口的任何直接实现。安全
Set是一个不能包含重复元素的集合。此接口对数学集合的抽象进行建模,并表示集合,例如纸牌集合。并发
List是一个有序的集合,能够包含重复的元素。您能够从其索引访问任何元素。该列表更像是具备动态长度的数组。
一个Map是键映射到值的对象。映射不能包含重复的键:每一个键最多能够映射到一个值。
其余一些接口Queue
,Dequeue
,Iterator
,SortedSet
,SortedMap
和ListIterator
。
Collection接口指定为一组元素对象。元素的维护方式取决于Collection的具体实现。例如,某些Collection实现(例如List)容许重复元素,而其余实现(例如Set)则不容许重复元素。
许多Collection实现都有Cloneable方法。可是,将其包含在Collection的全部实现中没有意义。这是由于Collection是抽象表示。重要的是。
在处理实际实现时,克隆或序列化的语义及其含义都会发挥做用。所以具体的实现应决定如何克隆或序列化它,甚至能够对其进行克隆或序列化。
所以,在全部实现中强制进行克隆和序列化的灵活性较差,限制也更大。具体实现应决定是否能够克隆或序列化。
尽管Map接口及其实现是Collections Framework的一部分,但Map不是集合,集合也不是Map。所以,Map扩展Collection是没有意义的,反之亦然。
若是Map扩展了Collection接口,那么元素在哪里?该映射包含key-value对,而且提供了一些方法来检索键或值的列表做为Collection,但它不适合“元素组”范式。
迭代器接口提供了对任何Collection进行迭代的方法。咱们可使用_iterator()_方法从Collection中获取迭代器实例。在Java Collections Framework中,迭代器代替了枚举。迭代器容许调用者在迭代过程当中从基础集合中删除元素。Java Collection迭代器提供了遍历集合元素的通用方法,并实现了Iterator Design Pattern。
枚举的速度是Iterator的两倍,而且使用的内存更少。枚举是很是基本的,适合基本需求。可是,与Enumeration相比,Iterator安全得多,由于它始终拒绝其余线程修改被其迭代的集合对象。
在Java Collections Framework中,迭代器代替了枚举。迭代器容许调用者从基础集合中删除Enumeration没法实现的元素。迭代器方法名称已获得改进,以使其功能更清晰。
考虑到Iterator的约定不保证迭代顺序,缘由尚不清楚。可是请注意,ListIterator确实提供了add操做,由于它确实保证了迭代的顺序。
能够在当前Iterator接口的顶部实现它,可是因为不多使用它,所以将它包含在每一个人都必须实现的接口中没有意义。
咱们能够经过两种不一样的方式遍历列表-使用迭代器和使用for-each循环。
List <String> strList = new ArrayList<>(); for(String obj:strList){ System.out.println(obj); } Iterator<String> it= strList.iterator(); while(it.hasNext()){ String obj = it.next(); System.out.println(obj); }
使用迭代器更加线程安全,由于它能够确保若是基础列表元素被修改,它将抛出异常ConcurrentModificationException
。
每当咱们尝试获取下一个元素时,迭代器fail-fast属性都会检查基础集合的结构是否有任何修改。若是找到任何修改,则抛出ConcurrentModificationException
。除了并行并发类(例如ConcurrentHashMap和CopyOnWriteArrayList)以外,Collection类中Iterator的全部实如今设计上都是fail-fast的。
Iterato fail-safe属性可与基础集合的克隆一块儿使用,所以不受集合中任何修改的影响。按照设计,java.util
包中的全部集合类都是fail-fast的,而其中的集合类java.util.concurrent
是fail-safe的。
fail-fast迭代器会抛出ConcurrentModificationException,而fail-safe迭代器毫不会抛出ConcurrentModificationException。
咱们可使用并发集合类来避免ConcurrentModificationException
在集合上进行迭代,例如使用CopyOnWriteArrayList而不是ArrayList。
Iterator接口声明了用于迭代集合的方法,可是其实现是Collection实现类的责任。每一个返回迭代器以进行遍历的集合类都有其本身的Iterator实现嵌套类。
这使集合类能够选择迭代器是fail-fast仍是fail-safe的。例如,ArrayList迭代器是fail-fast的,而CopyOnWriteArrayList迭代器是fail-safe的。
UnsupportedOperationException
是用于指示不支持该操做的异常。它普遍用于在JDK类,在集合框架java.util.Collections.UnmodifiableCollection
抛出该异常全部add
和remove
操做。
HashMap在Map.Entry
静态嵌套类实现中存储键值对。HashMap使用哈希算法,并在put
和get
方法中使用hashCode()和equals()方法。
当咱们put
经过传递键值对来调用方法时,HashMap使用带有哈希值的Key hashCode()来查找存储键值对的索引。该条目存储在LinkedList中,所以,若是已经存在一个条目,则使用equals()方法检查传递的键是否已存在,若是是,它将覆盖该值,不然它将建立一个新条目并存储此键值条目。
当咱们get
经过传递Key来调用method时,它再次使用hashCode()在数组中找到索引,而后使用equals()方法找到正确的Entry并返回其值。下图将清楚地解释这些细节。
有关HashMap的其余重要信息是容量,负载因子,阈值大小调整。HashMap的初始默认容量为16,负载系数为0.75。阈值是容量乘以负载因子,而且若是Map大小大于阈值,则每当咱们尝试添加条目时,HashMap都会将Map的内容从新映射为容量更大的新数组。容量始终是2的乘方,所以,若是您知道须要存储大量的键值对,例如在缓存数据库中的数据时,最好使用正确的容量和负载因子来初始化HashMap。 。
HashMap使用Key对象的hashCode()和equals()方法来肯定放置键值对的索引。当咱们尝试从HashMap中获取价值时,也会使用这些方法。若是这些方法的实现不正确,则两个不一样的Key可能会产生相同的hashCode()和equals()输出,在这种状况下,HashMap不会考虑将它们存储在不一样的位置,而是将其覆盖并覆盖它们。
一样,全部不存储重复数据的集合类都使用hashCode()和equals()查找重复项,所以正确实现它们很是重要。equals()和hashCode()的实现应遵循如下规则。
o1.equals(o2)
,那么o1.hashCode() == o2.hashCode()
应该永远如此true
。o1.hashCode() == o2.hashCode
是真的,这并不意味着o1.equals(o2)
会true
。咱们能够将任何类用做Map Key,可是在使用它们以前应考虑如下几点。
用户定义的键类的最佳实践是使其不可变,以即可以将hashCode()值缓存起来以提升性能。不可变的类还确保hashCode()和equals()未来不会更改,这将解决任何可变性问题。
例如,假设我有一个MyKey
用于HashMap键的类。
//传递的mykey name参数用于equals()和hashcode() MyKey key = new MyKey("Pankaj"); //假定hashCode=1234 myHashMap.put(key, "Value"); // 下面的代码将更改equals()和hashcode()的key // 可是它的位置不会改变 key.setName("Amit"); //假定新的hashCode=7890 //下面将返回null,由于HashMap将尝试查找键 //与存储在同一索引中,但因为密钥发生了变化, //不匹配,返回空。 myHashMap.get(new MyKey("Pankaj"));
这就是为何String和Integer大多用做HashMap键的缘由。
Map接口提供了三个集合视图:
“不积跬步,无以致千里”,但愿将来的你能:有梦为马 随处可栖!加油,少年!
关注公众号:「Java 知己」,天天更新Java知识哦,期待你的到来!