Java面试之集合专题(下)

1、哪一个List实现了最快插入?

LinkedList和ArrayList是另个不同变量列表的实现。ArrayList的优势在于动态的增长数组,非常适合初始时总长度未知的情况下使用。LinkedList的优势在于在中间位置插入和删除操作,速度是最快的。

LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。

ArrayList实现了可变大小的数组。它允许所有元素,包括null。 每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。

 

2、什么是CopyOnWriteArrayList,它与ArrayList有何不同?

CopyOnWriteArrayList是ArrayList的一个线程安全的变体,其中所有可变操作(add、set等等)都是通过对底层数组进行一次新的复制来实现的。相比较于ArrayList它的写操作要慢一些,因为它需要实例的快照。

CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能肯定很差,但是读操作因为操作的对象和写操作不是同一个对象,读之间也不需要加锁,读和写之间的同步处理只是在写完后通过一个简单的"="将引用指向新的数组对象上来,这个几乎不需要时间,这样读操作就很快很安全,适合在多线程里使用,绝对不会发生ConcurrentModificationException ,因此CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。

 

3、遍历一个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()); }

 

 

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

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

Array是指定大小的,而ArrayList大小是固定的

Array没有提供ArrayList那么多功能,比方addAll、removeAll和iterator等。虽然ArrayList明显是更好的选择。但也有些时候Array比較好用。

(1)假设列表的大小已经指定,大部分情况下是存储和遍历它们。

(2)对于遍历基本数据类型,虽然Collections使用自己主动装箱来减轻编码任务,在指定大小的基本类型的列表上工作也会变得非常慢。

(3)假设你要使用多维数组,使用[][]比List<List<>>更easy。

 

 

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

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

 

6、LinkedList和ArrayList的区别是什么?

  1. ArrayList是基于数组实现,LinkedList是基于链表实现
  2. ArrayList在查找时速度快,LinkedList在插入与删除时更具优势

 

7、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工具类轻易地获取同步列表和仅仅读列表。

 

8、ArrayList和LinkedList有何差别?

ArrayList和LinkedList两者都实现了List接口,可是它们之间有些不同。

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

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

(3)LinkedList比ArrayList消耗很多其他的内存,由于LinkedList中的每一个节点存储了前后节点的引用。

 

1、是否可以往 TreeSet 或者 HashSet 中添加 null 元素?

可以往 hashset 中添加一个 null

TreeSet 也允许一个 null值

hashmap也允许

 

2、EnumSet是什么?

java.util.EnumSet是使用枚举类型的集合实现。当集合创建时,枚举集合中的全部元素必须来自单个指定的枚举类型,能够是显示的或隐示的。EnumSet是不同步的,不同意值为null的元素。它也提供了一些实用的方法,比方copyOf(Collection c)、of(E first,E…rest)和complementOf(EnumSet s)。

 

1、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值相等的两个元素会形成一个链表。

 

2、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),使其实现同步。

 

3、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算法来实现同步问题,但其底层还是“数组+链表->红黑树”的实现。

 

4、HashSet的底层实现是什么?

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

 

5、LinkedHashMap的实现原理?

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

 

6、Map接口提供了哪些不同的集合视图?

Map接口提供三个集合视图:

(1)Set keyset():返回map中包括的全部key的一个Set视图。

集合是受map支持的,map的变化会在集合中反映出来,反之亦然。当一个迭代器正在遍历一个集合时。若map被改动了(除迭代器自身的移除操作以外),迭代器的结果会变为没有定义。

集合支持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作进行元素移除。从map中移除相应的映射。它不支持add和addAll操作。

(2)Collection values():返回一个map中包括的全部value的一个Collection视图。

这个collection受map支持的。map的变化会在collection中反映出来,反之亦然。当一个迭代器正在遍历一个collection时,若map被改动了(除迭代器自身的移除操作以外),迭代器的结果会变为没有定义。

集合支持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作进行元素移除,从map中移除相应的映射。它不支持add和addAll操作。

(3)Set<Map.Entry<K,V>> entrySet():返回一个map钟包括的全部映射的一个集合视图。

集合支持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作进行元素移除,从map中移除相应的映射。它不支持add和addAll操作。

 

7、怎样决定选用HashMap还是TreeMap?

对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而。假如你须要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,或许向HashMap中加入元素会更快。将map换为TreeMap进行有序key的遍历。

 

8、Map集合

实现类:HashMap、Hashtable、LinkedHashMap和TreeMap

HashMap 

HashMap是最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。因为键对象不可以重复,所以HashMap最多只允许一条记录的键为Null,允许多条记录的值为Null,是非同步的

 

Hashtable

Hashtable与HashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率较低。

线程安全,并且锁分离。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。

 

LinkedHashMap

LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历的时候会比HashMap慢,有HashMap的全部特性。

 

TreeMap

TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。不允许key值为空,非同步的;

 

9、HashMap与TreeMap

1、 HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。

2、在Map 中插入、删除和定位元素,HashMap是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和 equals()的实现。

两个map中的元素一样,但顺序不一样,导致hashCode()不一样。

同样做测试:

在HashMap中,同样的值的map,顺序不同,equals时,false;

而在treeMap中,同样的值的map,顺序不同,equals时,true,说明,treeMap在equals()时是整理了顺序了的。

 

10、HashTable与HashMap

1、同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的。

2、HashMap允许存在一个为null的key,多个为null的value 。

3、hashtable的key和value都不允许为null。

 

1、BlockingQueue是什么?

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

BlockingQueue接口是Java集合框架的一部分,主要用于实现生产者-消费者模式。我们不须要操心等待生产者有可用的空间。或消费者有可用的对象。由于它都在BlockingQueue的实现类中被处理了。

Java提供了集中BlockingQueue的实现,比方ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。

 

2、队列和栈是什么,列出它们的差别?

栈和队列两者都被用来预存储数据。java.util.Queue是一个接口,它的实现类在Java并发包中。队列同意先进先出(FIFO)检索元素,但并不是总是这样。Deque接口同意从两端检索元素。

栈与队列非常类似,但它同意对元素进行后进先出(LIFO)进行检索。Stack是一个扩展自Vector的类,而Queue是一个接口。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值