【搞定Java基础】之集合类面试题整理

因为集合类在Java基础知识中是非常重要的,也是面试中最常问到的,设计的问题也比较多,因此单独拿出来做面试题的整理,方便自己复习,也希望给看到此篇文章的你带来一定的帮助。文章内容均来自于网络,平时看到总结不错的题目,就收集在此。持续更新.......

先推荐几篇不错的文章:

1、Java集合类详解:https://blog.csdn.net/softwave/article/details/4166598


  • 集合类的基本框架

Java集合类框架图

---| Collection

---| List:有存储顺序,可重复

             ---| ArrayList:数组实现、查找快、增删慢

             ---| LinkedList:链表实现,增删快,查找慢

             ---| Vector:和ArrayList原理相同,但是线程安全,效率略低

---| Set:无存储顺序,不可重复

             ---| HashSet:哈希表实现、线程不安全、存取速度快

             ---| TreeSet:红黑树实现、默认对元素进行了排序

             ---| LinkedHashSet:哈希表和链表共同的实现、会保存存储顺序

---| Map

---| HashMap:采用哈希表实现、无序、可以存null

---| TreeMap:和TreeSet原理一样、可以对键进行排序、需要使用Comparable或者Comparator进行比较排序

---| HashTable:哈希表实现、线程同步、不可以存null


  • HashMap的工作原理是什么?

HashMap内部是通过一个数组实现的,只是这个数组比较特殊,数组里存储的元素是一个Entry实体(jdk 8为Node)。这个Entry实体主要包含 key、value 以及一个指向自身的next指针。

HashMap是基于hashing实现的,当我们进行put操作时,根据传递的key值得到它的hashcode,然后再用这个hashcode与数组的长度进行模运算,得到一个int值,就是Entry要存储在数组的位置(下标);当通过get方法获取指定key的值时,会根据这个key算出它的hash值(数组下标),根据这个hash值获取数组下标对应的Entry,然后判断Entry里的key,hash值或者通过equals()比较是否与要查找的相同,如果相同,返回value,否则的话,遍历该链表(有可能就只有一个Entry,此时直接返回null),直到找到为止,否则返回null。

HashMap之所以在每个数组元素存储的是一个链表,是为了解决hash冲突问题,当两个对象的hash值相等时,那么一个位置肯定是放不下两个值的,于是hashmap采用链表来解决这种冲突,hash值相等的两个元素会形成一个链表。


  • HashMap与HashTable的区别是什么?

1、HashTable基于Dictionary类,而HashMap是基于AbstractMap。Dictionary是任何可将键映射到相应值的类的抽象父类,而AbstractMap是基于Map接口的实现,它以最大限度地减少实现此接口所需的工作。(在java 8中我查看源码发现Hashtable并没有继承Dictionary,而且里面也没有同步方法,是不是java 8中Hashtable不在同步的了?有没有人解释一下?)

2、HashMap的key和value都允许为null,而Hashtable的key和value都不允许为null。HashMap遇到key为null的时候,调用putForNullKey方法进行处理,而对value没有处理;Hashtable遇到null,直接返回NullPointerException。

3、Hashtable是同步的,而HashMap是非同步的,但是我们也可以通过Collections.synchronizedMap(hashMap),使其实现同步。


  • CorrentHashMap的工作原理?

JDK 1.6版:

ConcurrenHashMap可以说是HashMap的升级版,ConcurrentHashMap是线程安全的,但是与Hashtablea相比,实现线程安全的方式不同。Hashtable是通过对hash表结构进行锁定,是阻塞式的,当一个线程占有这个锁时,其他线程必须阻塞等待其释放锁。ConcurrentHashMap是采用分离锁的方式,它并没有对整个hash表进行锁定,而是局部锁定,也就是说当一个线程占有这个局部锁时,不影响其他线程对hash表其他地方的访问。

具体实现: ConcurrentHashMap内部有一个Segment数组, 该Segment对象可以充当锁。Segment对象内部有一个HashEntry数组,于是每个Segment可以守护若干个桶(HashEntry),每个桶又有可能是一个HashEntry连接起来的链表,存储发生碰撞的元素。

每个ConcurrentHashMap在默认并发级下会创建包含16个Segment对象的数组,每个数组有若干个桶,当我们进行put方法时,通过hash方法对key进行计算,得到hash值,找到对应的segment,然后对该segment进行加锁,然后调用segment的put方法进行存储操作,此时其他线程就不能访问当前的segment,但可以访问其他的segment对象,不会发生阻塞等待。

JDK 1.8版 :

在jdk 8中,ConcurrentHashMap不再使用Segment分离锁,而是采用一种乐观锁CAS算法来实现同步问题,但其底层还是“数组+链表->红黑树”的实现。


  • 遍历一个List有哪些不同的方式?

    List<String> strList = new ArrayList<>();
    // 方法一:for-each
    for(String str:strList) {
        System.out.print(str);
    }

    // 方法二:use iterator 尽量使用这种 更安全(fail-fast)
    Iterator<String> it = strList.iterator();
    while(it.hasNext) {
        System.out.printf(it.next());
    }

  • fail-fast与fail-safe有什么区别?

Iterator的fail-fast属性与当前的集合共同起作用,因此它不会受到集合中任何改动的影响。Java.util包中的所有集合类都被设计为fail-fast的,而java.util.concurrent中的集合类都为fail-safe的。当检测到正在遍历的集合的结构被改变时,fail-fast迭代器抛出ConcurrentModificationException,而fail-safe迭代器从不抛出ConcurrentModificationException。


  • Array和ArrayList有何区别?什么时候更适合用Array?

1、Array可以容纳基本类型和对象,而ArrayList只能容纳对象;

2、Array是指定大小的,而ArrayList大小是固定的。


  • HashSet的底层实现是什么?

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


  • LinkedHashMap的实现原理?

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


  • LinkedList和ArrayList的区别是什么?

1、ArrayList是基于数组实现,LinkedList是基于链表实现;

2、ArrayList在查找时速度快,LinkedList在插入与删除时更具优势。


持续更新......

猜你喜欢

转载自blog.csdn.net/pcwl1206/article/details/84951861