Java学习手册:Java集合、泛型面试问题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/MaybeForever/article/details/95210941

1、Java学习手册:Java基础知识点
2、Java学习手册:Java面向对象面试问题
3、Java学习手册:Java集合、泛型面试问题
4、Java学习手册:Java并发与多线程面试问题
5、Java学习手册:Java虚拟机面试问题
6、Java学习手册:Java IO面试问题
7、Java学习手册:Java反射机制面试问题
8、Java学习手册:Java网络编程面试问题
9、Java学习手册:Java异常面试问题
10、Java学习手册:Java设计模式面试问题
11、Java学习手册:Java数据库面试问题

------------------------------模块一:Java集合面试题------------------------------------

1、Java集合框架是什么?说出⼀些集合框架的优点?

每种编程语⾔中都有集合,最初的Java版本包含⼏种集合类:Vector、Stack、HashTable和Array。随着集合的⼴泛使⽤,Java1.2提出了囊括所有集合接⼝、实现和算法的集合框架。在保证线程安全的情况下使⽤泛型和并发集合类,Java已经经历了很久。它还包括在Java并发包中,阻塞接⼝以及它们的实现。集合框架的部分优点如下:
(1)使⽤核⼼集合类降低开发成本,⽽⾮实现我们⾃⼰的集合类。
(2)随着使⽤经过严格测试的集合框架类,代码质量会得到提⾼。
(3)通过使⽤JDK附带的集合类,可以降低代码维护成本。
(4)复⽤性和可操作性。

2、集合框架中的泛型有什么优点?

Java1.5引⼊了泛型,所有的集合接⼝和实现都⼤量地使⽤它。泛型允许我们为集合提供⼀个可以容纳的对象类型,因此,如果你添加其它类型的任何元素,它会在编译时报错。这避免了在运⾏时出现ClassCastException,因为你将会在编译时得到报错信息。泛型也使得代码整洁,我们不需要使⽤显式转换和instanceOf操作符。它也给运⾏时带来好处,因为不会产⽣类型检查的字节码指令

3、Java集合框架的基础接⼝有哪些?

Collection为集合层级的根接⼝。⼀个集合代表⼀组对象,这些对象即为它的元素。Java平台不提供这个接⼝任何直接的实现。
Set是⼀个不能包含重复元素的集合。这个接⼝对数学集合抽象进⾏建模,被⽤来代表集合,就如⼀副牌。
List是⼀个有序集合,可以包含重复元素。你可以通过它的索引来访问任何元素。List更像⻓度动态变换的数组。
Map是⼀个将key映射到value的对象.⼀个Map不能包含重复的key:每个key最多只能映射⼀个value。
⼀些其它的接⼝有Queue、Dequeue、SortedSet、SortedMap和ListIterator。

4、为何Collection不从Cloneable和Serializable接⼝继承?

Collection接⼝指定⼀组对象,对象即为它的元素。如何维护这些元素由Collection的具体实现决定。例如,⼀些如List的Collection实现允许重复的元素,⽽其它的如Set就不允许。很多Collection实现有⼀个公有的clone⽅法。然⽽,把它放到集合的所有实现中也是没有意义的。这是因为Collection是⼀个抽象表现。重要的是实现。
当与具体实现打交道的时候,克隆或序列化的语义和含义才发挥作⽤。所以,具体实现应该决定如何对它进⾏克隆或序列化,或它是否可以被克隆或序列化。
在所有的实现中授权克隆和序列化,最终导致更少的灵活性和更多的限制。特定的实现应该决定它是否可以被克隆和序列化。

5、Collection与Collections的区别?

Java学习手册:Collection&Collections

6、为何Map接⼝不继承Collection接⼝?

尽管Map接⼝和它的实现也是集合框架的⼀部分,但Map不是集合,集合也不是Map。因此,Map继承Collection毫⽆意义,反之亦然。
如果Map继承Collection接⼝,那么元素去哪⼉?Map包含key-value对,它提供抽取key或value列表集合的⽅法,但是它不适合“⼀组对象”规范。
Map与Collection的区别,具体如下图所示:

在这里插入图片描述
补充:List和Set有什么区别和联系
List和Set都是Collection接口的子接口。List集合中的元素都是有序的并且允许重复元素;而Set集合中不允许存在重复元素,有的Set接口实现类中元素是有序的,有的是无序的。

7、Iterator是什么?

Java学习手册:迭代器(Iterator)
Iterator接⼝提供遍历任何Collection的接⼝。我们可以从⼀个Collection中使⽤迭代器⽅法来获取迭代器实例。迭代器取代了Java集合框架中的Enumeration。迭代器允许调⽤者在迭代过程中移除元素。

8、Enumeration和Iterator接⼝的区别?

Enumeration的速度是Iterator的两倍,也使⽤更少的内存。Enumeration是⾮常基础的,也满⾜了基础的需要。但是,与Enumeration相⽐,Iterator更加安全,因为当⼀个集合正在被遍历的时候,它会阻⽌其它线程去修改集合。
迭代器取代了Java集合框架中的Enumeration。迭代器允许调⽤者从集合中移除元素,⽽Enumeration不能做到。为了使它的功能更加清晰,迭代器⽅法名已经经过改善。

Enumeration速度是Iterator的2倍,同时占⽤更少的内存。但是,Iterator远远⽐Enumeration安全,因为其他线程不能够修改正在被iterator遍历的集合⾥⾯的对象。同时,Iterator允许调⽤者删除底层集合⾥⾯的元素,这对Enumeration来说是不可能的。

9、为何没有像Iterator.add()这样的⽅法,向集合中添加元素?

语义不明,已知的是,Iterator的协议不能确保迭代的次序。然⽽要注意,ListIterator没有提供⼀个add操作,它要确保迭代的顺序。

10、为何迭代器没有⼀个⽅法可以直接获取下⼀个元素,⽽不需要移动游标?

它可以在当前Iterator的顶层实现,但是它⽤得很少,如果将它加到接⼝中,每个继承都要去实现它,这没有意义。

11、Iterater和ListIterator之间有什么区别?

(1)我们可以使⽤Iterator来遍历Set和List集合,⽽ListIterator只能遍历List
(2)Iterator只可以向前遍历,⽽ListIterator可以双向遍历
(3)ListIterator从Iterator接⼝继承,然后添加了⼀些额外的功能,⽐如添加⼀个元素、替换⼀个元素、获取前⾯或后⾯元素的索引位置。

12、遍历⼀个List有哪些不同的⽅式?

List<String> strList =  new ArrayList<>();
//使用for-each循环
for(String obj:strList){
	System.out.println(obj);
}
//使用Iterator
Iterator<String> it = strList.iterator();
while(it.hasNext()){
	String obj = it.next();
	System.out.println(obj);
}

使⽤迭代器更加线程安全,因为它可以确保,在当前遍历的集合元素被更改的时候,它会抛出ConcurrentModificationException。

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

每次我们尝试获取下⼀个元素的时候,Iterator fail-fast属性检查当前集合结构⾥的任何改动。如果发现任何改动,它抛出ConcurrentModificationException。Collection中所有Iterator的实现都是按failfast来设计的(ConcurrentHashMap和CopyOnWriteArrayList这类并发集合类除外)。

14、fail-fast(快速失败)与fail-safe(安全失败)有什么区别?

Iterator的fail-fast属性与当前的集合共同起作⽤,因此它不会受到集合中任何改动的影响。Java.util包中的所有集合类都被设计为fail-fast的,⽽java.util.concurrent中的集合类都为fail-safe的。Fail-fast迭代器抛出ConcurrentModificationException,⽽fail-safe迭代器从不抛出ConcurrentModificationException。

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

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

在遍历⼀个集合的时候,我们可以使⽤并发集合类来避免ConcurrentModificationException,⽐如使⽤CopyOnWriteArrayList,⽽不是ArrayList。

16、为何Iterator接⼝没有具体的实现?

Iterator接⼝定义了遍历集合的⽅法,但它的实现则是集合实现类的责任。每个能够返回⽤于遍历的Iterator的集合类都有它⾃⼰的Iterator实现内部类。
这就允许集合类去选择迭代器是fail-fast还是fail-safe的。⽐如,ArrayList迭代器是fail-fast的,⽽CopyOnWriteArrayList迭代器是fail-safe的。

17、UnsupportedOperationException是什么?

UnsupportedOperationException是⽤于表明操作不⽀持的异常。在JDK类中已被⼤量运⽤,在集合框架java.util.Collections.UnmodifiableCollection将会在所有add和remove操作中抛出这个异常。

18、在Java中,HashMap是如何⼯作的?

(1)HashMap工作原理
HashMap的底层存储着Entry(hash,key,value,next) 对象,通过Hash算法计算对应Key的hash值存储对象,将key/value传给put()方法时,它调用hashCode计算hash值,从而得到对应在Table中的位置,如果存储的Key的hash值所对应的Table位置已经存在元素,HashMap通过链表将产生的碰撞冲突的元素组织起来,当冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提高速度。
在这里插入图片描述
(2)如何实现HashMap的同步?
HashMap可以通过如下方法来达到同步效果:

Map m = Collections.synchronizedMap(new HashMap);

具体而言,该方法返回一个同步的Map,该Map封装了底层的HashMap的所有方法,使得底层的HashMap即使是在多线程的环境中也是安全的。

(3)当两个对象的hashcode相同,即发生碰撞时,HashMap如何处理?
当两个对象的hashcode相同,它们的bucket位置相同,HashMap会用链表或是红黑树来存储对象。Entry类里有一个next属性,作用是指向下一个Entry。第一个键值对A进来,通过计算其key的hash得到index,记做Entry[index]=A。一会又进来一个键值对B,通过计算其key的hash也是index,HashMap会将B.next=A, Entry[index]=B.如果又进来C,其key的hash也是index,会将C.next=B, Entry[index]=C.这样bucket为index的地方存放了A、B、C三个键值对,它们能过next属性链在一起。数组中存储的是最后插入的元素,其他元素都在后面的链表里

(4)如果两个键的hashcode相同,如何获取值对象?
当调用get方法时,Hashmap会使用键对象的hashcode找到bucket位置,找到bucket位置后,会调用key.equals() 方法去找到链表中正确的节点,最终找到值对象。

(5)hashMap如何扩容?
HashMap默认负载因为是0.75,当一个map填满了75%的bucket时,和其他集合类一样,将会创建原来HashMap大小两倍的bucket数组,来重新调整HashMap的大小,并将原来的对象放入新的bucket数组中。

19、hashCode()和equals()⽅法有何重要性?

Java学习手册:“==”&equals&hashCode
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。

由于为了提高程序的效率才实现了hashcode方法,先进行hashcode的比较,如果不同,那么就不必再进行equals的比较了,这样就大大减少了equals比较的次数,这对于解决需要比较的数量很大的问题的效率提高是很明显的。(之所以有hashCode方法,因为在批量的对象比较中,hashCode比较速度比equals快)

20、我们能否使⽤任何类作为Map的key?

我们可以使⽤任何类作为Map的key,然⽽在使⽤它们之前,需要考虑以下⼏点:
(1)如果类重写了equals()⽅法,它也应该重写hashCode()⽅法。
(2)类的所有实例需要遵循与equals()和hashCode()相关的规则。请参考之前提到的这些规则。
(3)如果⼀个类没有使⽤equals(),你不应该在hashCode()中使⽤它。
(4)⽤⼾⾃定义key类的最佳实践是使之为不可变的,这样,hashCode()值可以被缓存起来,拥有更好的性能。不可变的类也可以确保hashCode()和equals()在未来不会改变,这样就会解决与可变相关的问题了。
⽐如,我有⼀个类MyKey,在HashMap中使⽤它。

//传递给MyKey的name参数被用于equals()和hashCode()中
MyKey key = new MyKey('Pankaj');//assume hashCode=1234
myHashMap.put(key, 'Value');
//以下的代码会改变key的hashCode()和equals()值
key.setName('Amit');//assume hashCode=7890
//下面会返回null,因为HashMap会尝试查找存储同样索引的key,而key已经改变了,匹配失败,返回null
myHashMap.get(new MyKey('Pankaj'));

那就是为何String和Integer被作为HashMap的key⼤量使⽤。

21、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钟包含的所有映射的⼀个集合视图。这个集合受map⽀持的,map的变化会在collection中反映出来,反之亦然。当⼀个迭代器正在遍历⼀个集合时,若map被修改了(除迭代器⾃⾝的移除操作,以及对迭代器返回的entry进⾏setValue外),迭代器的结果会变为未定义。集合⽀持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作进⾏元素移除,从map中移除对应的映射。它不⽀持add和addAll操作。

22、HashMap和HashTable有何不同?

(1)HashMap允许key和value为null,⽽HashTable不允许
(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。

23、HashMap、HashTable、TreeMap和WeakHashMap的区别?

Java学习手册:HashMap&HashTable&TreeMap&WeakHashMap

24、如何决定选⽤HashMap还是TreeMap?

对于在Map中插⼊、删除和定位元素这类操作,HashMap是最好的选择。然⽽,假如你需要对⼀个有序的key集合进⾏遍历,TreeMap是更好的选择。基于你的collection的⼤⼩,也许向HashMap中添加元素会更快,将map换为TreeMap进⾏有序key的遍历。

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

注:ArrayList与LinkedList的读写时间复杂度
(1)ArrayList 是线性表(数组)

  • get() 直接读取第几个下标,复杂度 O(1)
  • add(E) 添加元素,直接在后面添加,复杂度O(1)
  • add(index, E) 添加元素,在第几个元素后面插入,后面的元素需要向后移动,复杂度O(n)
  • remove()删除元素,后面的元素需要逐个移动,复杂度O(n)

(2)LinkedList 是链表的操作

  • get() 获取第几个元素,依次遍历,复杂度O(n)
  • add(E) 添加到末尾,复杂度O(1)
  • add(index, E) 添加第几个元素后,需要先查找到第几个元素,直接指针指向操作,复杂度O(n)
  • remove()删除元素,直接指针指向操作,复杂度O(1)

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

Array可以容纳基本类型和对象,⽽ArrayList只能容纳对象
Array是指定⼤⼩的,⽽ArrayList⼤⼩是固定的
Array没有提供ArrayList那么多功能,⽐如addAll、removeAll和iterator等。尽管ArrayList明显是更好的选择,但也有些时候Array⽐较好⽤。
(1)如果列表的⼤⼩已经指定,⼤部分情况下是存储和遍历它们。
(2)对于遍历基本数据类型,尽管Collections使⽤⾃动装箱来减轻编码任务,在指定⼤⼩的基本类型的列表上⼯作也会变得很慢。
(3)如果你要使⽤多维数组,使⽤[][]⽐List<List<>>更容易。

27、ArrayList和LinkedList有何区别?

ArrayList和LinkedList两者都实现了List接⼝,但是它们之间有些不同。
(1)ArrayList是由Array所⽀持的基于⼀个索引的数据结构,所以它提供对元素的随机访问,复杂度为O(1),但LinkedList存储⼀系列的节点数据,每个节点都与前⼀个和下⼀个节点相连接。所以,尽管有使⽤索引获取元素的⽅法,内部实现是从起始点开始遍历,遍历到索引的节点然后返回元素,时间复杂度为O(n),⽐ArrayList要慢。
(2)与ArrayList相⽐,在LinkedList中插⼊、添加和删除⼀个元素会更快,因为在⼀个元素被插⼊到中间的时候,不会涉及改变数组的⼤⼩,或更新索引。
(3)LinkedList⽐ArrayList消耗更多的内存,因为LinkedList中的每个节点存储了前后节点的引⽤。

28、ArrayList、LinkedList和Vector的区别?

Java学习手册:ArrayList&LinkedList&Vector

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

ArrayListHashMapTreeMapHashTable类提供对元素的随机访问。

30、EnumSet是什么?

java.util.EnumSet是使⽤枚举类型的集合实现。当集合创建时,枚举集合中的所有元素必须来⾃单个指定的枚举类型,可以是显⽰的或隐⽰的。EnumSet是不同步的,不允许值为null的元素。它也提供了⼀些有⽤的⽅法,⽐如copyOf(Collection c)、of(E first,E…rest)和complementOf(EnumSet s)。

31、哪些集合类是线程安全的?

Vector、HashTable、Properties和Stack是同步类,所以它们是线程安全的,可以在多线程环境下使⽤。Java1.5并发API包括⼀些集合类,允许迭代时修改,因为它们都⼯作在集合的克隆上,所以它们在多线程环境中是安全的。

32、并发集合类是什么?

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

33、BlockingQueue是什么?

Java.util.concurrent.BlockingQueue是⼀个队列,在进⾏检索或移除⼀个元素的时候,它会等待队列变为⾮空;当在添加⼀个元素时,它会等待队列中的可⽤空间。BlockingQueue接⼝是Java集合框架的⼀部分,主要⽤于实现⽣产者-消费者模式。我们不需要担⼼等待⽣产者有可⽤的空间,或消费者有可⽤的对象,因为它都在BlockingQueue的实现类中被处理了。Java提供了集中BlockingQueue的实现,⽐如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。

34、队列和栈是什么,列出它们的区别?

栈和队列两者都被⽤来预存储数据。java.util.Queue是⼀个接⼝,它的实现类在Java并发包中。队列允许先进先出(FIFO)检索元素,但并⾮总是这样。Deque接⼝允许从两端检索元素。
栈与队列很相似,但它允许对元素进⾏后进先出(LIFO)进⾏检索。
Stack是⼀个扩展⾃Vector的类,⽽Queue是⼀个接⼝。

35、Comparable和Comparator接⼝是什么?

如果我们想使⽤Array或Collection的排序⽅法时,需要在⾃定义类⾥实现Java提供Comparable接⼝。Comparable接⼝有compareTo(T OBJ)⽅法,它被排序⽅法所使⽤。我们应该重写这个⽅法,如果“this”对象⽐传递的对象参数更⼩、相等或更⼤时,它返回⼀个负整数、0或正整数。但是,在⼤多数实际情况下,我们想根据不同参数进⾏排序。⽐如,作为⼀个CEO,我想对雇员基于薪资进⾏排序,⼀个HR想基于年龄对他们进⾏排序。这就是我们需要使⽤Comparator接⼝的情景,因为Comparable.compareTo(Object o)⽅法实现只能基于⼀个字段进⾏排序,我们不能根据对象排序的需要选择字段。Comparator接⼝的compare(Object o1, Object o2)⽅法的实现需要传递两个对象参数,若第⼀个参数⽐第⼆个⼩,返回负整数;若第⼀个等于第⼆个,返回0;若第⼀个⽐第⼆个⼤,返回正整数

36、Comparable和Comparator接⼝有何区别?

Comparable和Comparator接⼝被⽤来对对象集合或者数组进⾏排序。
Comparable接⼝被⽤来提供对象的⾃然排序,我们可以使⽤它来提供基于单个逻辑的排序
Comparator接⼝被⽤来提供不同的排序算法,我们可以选择需要使⽤的Comparator来对给定的对象集合进⾏排序

37、我们如何对⼀组对象进⾏排序?

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

38、当⼀个集合被作为参数传递给⼀个函数时,如何才可以确保函数不能修改它?

在作为参数传递之前,我们可以使⽤Collections.unmodifiableCollection(Collection c)⽅法创建⼀个只读集合,这将确保改变集合的任何操作都会抛出UnsupportedOperationException。

39、Collections类是什么?

Java.util.Collections是⼀个⼯具类仅包含静态⽅法,它们操作或返回集合。它包含操作集合的多种方法,返回⼀个由指定集合⽀持的新集合和其它⼀些内容。这个类包含集合框架算法的⽅法,⽐如折半搜索、排序、混编和逆序等。

40、我们如何从给定集合那⾥创建⼀个synchronized的集合?

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

41、集合框架⾥实现的通⽤算法有哪些?

Java集合框架提供常⽤的算法实现,⽐如排序和搜索。Collections类包含这些⽅法实现。⼤部分算法是操作List的,但⼀部分对所有类型的集合都是可⽤的。部分算法有排序、搜索、混编、最⼤最⼩值。

42、⼤写的O是什么?举⼏个例⼦?

⼤写的O描述的是,就数据结构中的⼀系列元素⽽⾔,⼀个算法的性能。Collection类就是实际的数据结构,我们通常基于时间、内存和性能,使⽤⼤写的O来选择集合实现。⽐如:例⼦1:ArrayList的get(index i)是⼀个常量时间操作,它不依赖list中元素的数量。所以它的性能是O(1)。例⼦2:⼀个对于数组或列表的线性搜索的性能是O(n),因为我们需要遍历所有的元素来查找需要的元素。

43、Java集合框架使用过程中有哪些好的建议?

(1)根据需要选择正确的集合类型。⽐如,如果指定了⼤⼩,我们会选⽤Array⽽⾮ArrayList。如果我们想根据插⼊顺序遍历⼀个Map,我们需要使⽤TreeMap。如果我们不想重复,我们应该使⽤Set。
(2)⼀些集合类允许指定初始容量,所以如果我们能够估计到存储元素的数量,我们可以使⽤它,就避免了重新哈希或⼤⼩调整。
(3)基于接⼝编程,⽽⾮基于实现编程,它允许我们后来轻易地改变实现。
(4)总是使⽤类型安全的泛型,避免在运⾏时出现ClassCastException。
(5)使⽤JDK提供的不可变类作为Map的key,可以避免⾃⼰实现hashCode()和equals()。
(6)尽可能使⽤Collections⼯具类,或者获取只读、同步或空的集合,⽽⾮编写⾃⼰的实现。它将会提供代码重⽤性,它有着更好的稳定性和可维护性。

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

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

45、Java中遍历Map的四种方式

Java学习手册:遍历Map的五种方法

46、并发集合了解哪些?

(1)并发List,包括Vector和CopyOnWriteArrayList是两个线程安全的List,Vector读写操作都用了同步,CopyOnWriteArrayList在写的时候会复制一个副本,对副本写,写完用副本替换原值,读时不需要同步。

(2)并发Set,CopyOnWriteArraySet基于CopyOnWriteArrayList来实现的,不允许存在重复的对象。

(3)并发Map,ConcurrentHashMap,内部实现了锁分离,get操作是无锁的。

(4)并发Queue,ConcurrentLinkedQueue适用于高并发场景下的队列,通过无锁方式实现。 BlockingQueue阻塞队列,应用场景,生产者-消费者模式,若生产快于消费,生产队列装满时会阻塞,等待消费。

(5)并发Deque, LinkedBlockingDueue没有进行读写锁分离,同一时间只能有一个线程对其操作。

(6)并发锁重入锁ReentrantLock,互斥锁,一次最多只能一个线程拿到锁。

(7)读写锁ReadWriteLock,有读取和写入锁两种,读取允许多个读取线程同时持有,而写入只能有一个线程持有。

47、集合Set实现Hash怎么防止碰撞?

HashSet底层实现是HashMap,HashMap如果两个不同Key对象的hashCode()值相等,会用链表存储,HashSet也一样。

当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

48、为什么HashMap线程不安全?(Hash碰撞与扩容导致)

HashMap的底层存储结构是一个Entry数组,每个Entry又是一个单链表,一旦发生Hash冲突的的时候,HashMap采用链地址法解决碰撞冲突,因为HashMap的put()方法不是同步的,所以他的扩容方法也不是同步的,在扩容过程中,会新生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。当多个线程同时检测到Hashmap需要扩容的时候就会同时调用resize()操作,各自生成新的数组并rehash后赋给该map底层的数组table,结果最终只有最后一个线程生成的新数组被赋给table变量,其他线程的均会丢失。而且当某些线程已经完成赋值而其他线程刚开始的时候,就会用已经被赋值的table作为原始数组,这样也会有问题。扩容的时候可能会引发链表形成环状结构。

49、HashMap如何保证元素均匀分布?

hash & (length-1)

通过Key值的hashCode值和hashMap长度-1做与运算。

解释:hashmap中的元素,默认情况下,数组大小为16,也就是2的4次方,如果要自定义HashMap初始化数组长度,也要设置为2的n次方大小,因为这样效率最高。因为当数组长度为2的n次幂的时候,不同的key算出的index相同的几率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表,这样查询效率也就较高了。

------------------------------模块二:Java泛型面试题------------------------------------

1、什么是泛型?

Java学习手册:泛型

猜你喜欢

转载自blog.csdn.net/MaybeForever/article/details/95210941