面试题:Java集合框架

主题 链接
Java基础知识 面试题
Java集合框架 面试题
Java并发编程 面试题
Redis 面试题

常用集合类的使用

  • Collection接口的子接口包括:Set接口和List接口
  • Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
  • Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
  • List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等

HashMap与HashTable的区别?

  • HashMap没有考虑同步,是线程不安全的;Hashtable使用了synchronized关键字,是线程安全的;
  • HashMap允许K/V都为null;后者K/V都不允许为null;
  • HashMap继承自AbstractMap类;而Hashtable继承自Dictionary类;

JDK1.8以后HashMap的put方法的具体流程?

当 HashMap 中有大量的元素都存放到同一个桶中时,这个桶下有一条长长的链表,这个时候 HashMap 就相当于一个单链表,假如单链表有 n 个元素,遍历的时间复杂度就是 O(n),完全失去了它的优势。
针对这种情况,JDK 1.8 中引入了 红黑树(查找时间复杂度为 O(logn))来优化这个问题。
在这里插入图片描述
在这里插入图片描述

ArrayList、LinkList、Vetor的区别

List主要有ArrayList、LinkedList与Vector几种实现。
这三者都实现了List 接口,使用方式也很相似,主要区别在于因为实现方式的不同,所以对不同的操作具有不同的效率。

  • ArrayList
    是一个可改变大小的数组.当更多的元素加入到ArrayList中时,其大小将会动态地增长.内部的元素可以直接通过get与set方法进行访问,因为ArrayList本质上就是一个数组.
  • LinkedList
    是一个双链表,在添加和删除元素时具有比ArrayList更好的性能.但在get与set方面弱于ArrayList.当然,这些对比都是指数据量很大或者操作很频繁的情况下的对比,如果数据和运算量很小,那么对比将失去意义.
  • Vector
    和ArrayList类似,但属于强同步类。如果你的程序本身是线程安全的(thread-safe,没有在多个线程之间共享同一个集合/对象),那么使用ArrayList是更好的选择。
  • Vector和ArrayList在更多元素添加进来时会请求更大的空间。Vector每次请求其大小的双倍空间,而ArrayList每次对size增长50%.而 LinkedList 还实现了 Queue 接口,该接口比List提供了更多的方法,包offer(),peek(),poll()等. 注意:默认情况下ArrayList的初始容量非常小,所以如果可以预估数据量的话,分配一个较大的初始值属于最佳实践,这样可以减少调整大小的开销。

HashMap、HashTable、ConcurrenHashMap的区别

HashMap和HashTable有何不同?

  • 线程安全: HashTable 中的方法是同步的,而HashMap中的方法在默认情况下是非同步的。在多线程并发的环境下,可以直接使用HashTable,但是要使用HashMap的话就要自己增加同步处理了。
  • 继承关系: HashTable是基于陈旧的Dictionary类继承来的。
    HashMap继承的抽象类AbstractMap实现了Map接口。
  • 允不允许null值:HashTable中,key和value都不允许出现null值,否则会抛出NullPointerException异常。HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。
  • 默认初始容量和扩容机制: HashTable中的hash数组初始大小是11,增加的方式是old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。
  • 哈希值的使用不同 : HashTable直接使用对象的hashCode。 HashMap重新计算hash值。
  • 遍历方式的内部实现上不同 : Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。 HashMap 实现Iterator,支持fast-fail,Hashtable的 Iterator 遍历支持fast-fail,用 Enumeration不支持 fast-fail

HashMap 和 ConcurrentHashMap 的区别?

  • ConcurrentHashMap和HashMap的实现方式不一样,虽然都是使用桶数组实现的,但是还是有区别,ConcurrentHashMap对桶数组进行了分段,而HashMap并没有。
  • ConcurrentHashMap在每一个分段上都用锁进行了保护。HashMap没有锁机制。所以,前者线程安全的,后者不是线程安全的。

PS:以上区别基于jdk1.8以前的版本。

Java8中stream相关用法

Stream不是集合元素,更不是数据结构,它跟数据的存储没有任何关系,它只是一种针对数据的计算而存在的,可以把它看成是更高级的迭代器。

不同版本JDK的HashMap的实现的区别以及原因

Hashmap的结构,1.7和1.8有哪些区别

Collection和Collections的区别

  • Collection:是集合类的上层接口。本身是一个Interface,里面包含了一些集合的基本操作。Collection接口是Set接口和List接口的父接口
  • Collections:Collections是一个集合框架的帮助类,里面包含一些对集合的排序,搜索以及序列化的操作。

Arrays.asList获得的List使用时需要注意什么

Arrays.asList得到的List它的长度是不能改变的。当你向这个List添加或删除一个元素时(例如 list.add(“d”);)程序就会抛出异常(java.lang.UnsupportedOperationException)。
public static List asList(T… a) {
return new ArrayList<>(a);
}

当你看到这段代码时可能觉得没啥问题啊,不就是返回了一个ArrayList对象吗?问题就出在这里。这个ArrayList不是java.util包下的,而是java.util.Arrays.ArrayList,显然它是Arrays类自己定义的一个内部类!这个内部类没有实现add()、remove()方法,而是直接使用它的父类AbstractList的相应方法。而AbstractList中的add()和remove()是直接抛出java.lang.UnsupportedOperationException异常的!

fail-fast和fail-safe

  • 线程不安全的类,并发情况下可能会出现快速失败;线程安全的类,可能会出现安全失败
  • 一个线程在遍历,另一个线程在添加、删除或修改,就会出现并发修改的问题
  • 当遍历时检测到并发修改,就会抛出异常:concurrentmodificationException,这就是快速失败
  • ArrayList.iterator()返回一个迭代器对象,其中使用一个int类型的expectedModCount记录状态,当发生添加、删除、修改操作时会更改这个值,当遍历时调用next()会检查这个值跟开始遍历时是否一致,发现expectedModCount发生了变化,就意味着有并发修改,这时候就抛出异常iterator.remove()方法没有进行modCount值的检查,并且手动把expectedModCount值修改成了modCount值,这又保证了下一次迭代的正确。
  • 而fail-safe是一个概念,并发容器的并发修改不会抛出异常,这和其实现有关。并发容器的iterate方法返回的iterator对象,内部都是保存了该集合对象的一个快照副本,并且没有modCount等数值做检查。这也造成了并发容器的iterator读取的数据是某个时间点的快照版本。你可以并发读取,不会抛出异常,但是不保证你遍历读取的值和当前集合对象的状态是一致的!这就是安全失败的含义。

CopyOnWriteArrayList、ConcurrentSkipListMap

ConcurrentSkipListMap与CopyOnWriteArrayList

Hashmap 什么时候进行扩容呢?

  • 默认大小为16、负载因子为0.75,即超过12就扩容,容量扩大一倍
  • 新建hashmap时设置初始大小,假如有1000个元素,不能设置1000,因为元素数量为750时就会自动扩容,要避免自动扩容,要让元素数量不超过初始容量的0.75
  • 扩容时会重新计算元素在数组中的位置,尽量避免扩容
  • Hash的公式—> index = HashCode(Key) & (Length - 1)
  • 因为resize的赋值方式,也就是使用了单链表的头插入方式,同一位置上新元素总会被放在链表的头部位置,在旧数组中同一条Entry链上的元素,通过重新计算索引位置后,有可能被放到了新数组的不同位置上。会形成环形列表。
  • 使用头插会改变链表的上的顺序,但是如果使用尾插,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题了。Java8在同样的前提下并不会引起死循环,原因是扩容转移后前后链表顺序不变,保持之前节点的引用关系。

HashMap的默认初始化长度是多少?为什么?

因为在使用2的幂的数字的时候,Length-1的值是所有二进制位全为1,这种情况下,index的结果等同于HashCode后几位的值。
只要输入的HashCode本身分布均匀,Hash算法的结果就是均匀的。
这是为了实现均匀分布。
https://blog.csdn.net/qq_36520235/article/details/82417949

哈希表如何解决Hash冲突?

在这里插入图片描述

为什么 HashMap 中 String、Integer 这样的包装类适合作为 key 键

在这里插入图片描述

HashMap 中的 key若为 Object类型, 则需实现哪些方法?

在这里插入图片描述

List、Map、Set 三个接口,存取元素时,各有什么特点?

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

发布了45 篇原创文章 · 获赞 18 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_26761587/article/details/105508174
今日推荐