Java面试题2 集合容器
自己尝试通过打字来回答一些网上常见的面试题,答案仅代表我自己的观点
18. java 容器都有哪些?#
- 集合类(Collections):Set、HashSet、LinkedHashSet、TreeSet、List、LinkedList、ArrayList、Vector、Queue、PriorityQueue、Stack
- 映射类(Map):HashMap、TreeMap、HashTable
- 并发容器:ConcurrentHashMap、ConcurrentSkipListMap、CopyOnWriteArrayList
需要注意,Stack是在Vector下的子类
19. Collection 和 Collections 有什么区别?#
Java中有很多XXX
、XXXs
这种命名的类,XXX
表达一类东西,带s
的XXXs
表示对这类东西提供的工具类,如Array
、Arrays
,Executor
、Executors
。
所以Collection
就是代表集合类的一个接口,规范了集合类数据结构的一些行为,Collections
就是集合类的工具方法,其中包括:
Collections.synchronizedXXX
:创建同步集合的工厂方法Collections.unmodifiableXXX
:创建不可变集合的工厂方法Collections.sort
:排序集合- ...
20. List、Set、Map 之间的区别是什么?#
List
是列表,用于保存一类数据Set
是集合,同样用于保存一类数据,不同的是,集合中的数据是不能重复的,这和数学中的集合定义一致。Map
是映射,用于将一类东西映射到一类东西,比如Map<String, Product>
用于保存从字符串到商品的映射。
21. HashMap 和 Hashtable 有什么区别?#
Hashtable
是Java早前的一个容器类,它是同步的,也就是说它其中的所有方法通过synchronized
进行简单的同步保护,所有线程必须同步访问。
HashMap
没有这种保护。
- hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
- hashTable同步的,而HashMap是非同步的,效率上逼hashTable要高。
- hashMap允许空键值,而hashTable不允许。
22. 如何决定使用 HashMap 还是 TreeMap?#
HashMap是基于散列的,它的查找、删除、插入等各种操作的时间复杂度都可以被看成是的,是很快的,但它的缺点是由于使用了散列算法,保存到数据被随机的散列到一个大空间中,想让它有序几乎是不可能。
TreeMap是基于树的,它的各种操作的时间复杂度稍慢一些,可以被看成是的,但也很快了,由于使用树,所以它天生具有有序性,只不过它稍慢一些。
所以,具体看你的需求,如果你不需要有序性,或者很少需要排序的话,而且数据集很大,你可以使用HashMap,如果你频繁的需要有序性,使用TreeMap更好。
23. 说一下 HashMap 的实现原理?#
我没看过HashMap的源码,我觉得没必要,但我可以浅谈一下哈希表的实现原理和需要注意的事项,以及我们在Java这种编程语言中想实现一个哈希表,有什么是我们可以直接利用的。
首先,Hash技术是通过一个哈希函数将一个值映射到一个空间中的某一位置上,哈希函数的映射越均匀越好。
一般情况下,这个空间是通过一个底层数组实现的,哈希函数将某种值映射到数组的一个下标上。并非所有哈希函数都是通过求余来实现的,但在使用求余技术的哈希函数中,底层数组最好选用一个素数,这可以把公因数的个数降低到最少,从而降低冲突。
提到冲突,你永远无法把宇宙存进一个有限大小的底层数组中,所以,只要哈希函数的作用域够大,值域有限,那么它必定有一些不同的值通过哈希函数被映射到相同的下标上,这种情况下需要采取一种办法来解决冲突。通常都是在底层数组中保存一个链表,所有具有相同哈希值的原值被放到一个链表中,查找时找到下标后顺序查找链表以找到原值。在这种情况下,如果哈希函数设计的不好,那么HashMap的时间复杂度很有可能降低到O(n)
,这和线性查找就没区别了,所以一些高性能的HashMap实现就可能把链表替换成树,而且还得是平衡树,比如AVL或红黑树。听我的同学念叨,Java中的HashMap就使用了红黑树。
还有就是,发生冲突的比率也决定了HashMap的性能,除了哈希函数的设计以外,这和作用域和值域的比例有关,也就是说如果底层数组的大小比当前Map中存储的数据量小太多的话,冲突就会频繁发生。所以一个高性能的HashMap应该具有动态扩容的功能,即当冲突发生的太频繁时,加大底层数组的大小。我听室友好像也念叨过,Java里的HashMap有个什么扩容因子,是HashMap扩容的阈值。需要注意的是,当底层数组大小改变时,之前已经保存的所有数据都需要重新进行哈希计算,这是个耗时操作,好在使用摊还分析计算的话,均摊到每个元素上的成本很低。
下面说说在Java中实现一个Hash表我们有什么可以利用的。首先,如果使用底层数组,那么哈希函数的设计就面临一个如何把各种类型的数据转换成数组下标的难题。如何把String
转换成一个数组下标,并且保证它能足够均匀的将所有字符串分布到底层数组中?解决了String
类,那Date
类呢???这个问题显然不是HashMap的设计者该考虑的,因为HashMap作为一个通用的容器,它可以容纳任何对象类型,它不可能了解所有的对象类型。所以将数据均匀分布成下标,这就是对应类该考虑的问题。我们可以利用对象的hashCode
方法来直接得到一个整数,然后让这个整数余数组长度,就直接得到了一个数组下标。我们要做的只有这些。
然后,对于冲突,我们还可以用equals
方法来判断两个对象是否相等,因为判断对象的等价性也不应该是HashMap的职责,只有对应的类才最了解应该怎么判断。
24. 说一下 HashSet 的实现原理?#
同上
我记得我室友问过我为什么有了HashMap
还要HashSet
。我说它俩只是两个不同的工具,虽然它们的原理相同,并且你如果不介意多写点代码的话,你完全可以用HashMap
来干HashSet
的活,用HashSet
来干HashMap
的活。对于使用者,你只需要使用Set
存储集合Map
存储映射就行了,搞不好Java里的HashSet
底层都是直接用HashMap
实现的。
妈的今天一看确实是这样。。。
25. ArrayList 和 LinkedList 的区别是什么?#
ArrayList基于数组,对于随机查询和修改,它的时间复杂度是,对于随机插入和删除,由于要移动后面的元素,所以复杂度是。有容量限制,当满了的时候需要扩容。
LinkedList基于链表,随机查询一个元素的时间复杂度是,但如果有那种从头或尾部插入或删除的操作它应该只需要的时间复杂度,所以用它实现队列和栈啥的正好。它没有容量限制。
26. 如何实现数组和 List 之间的转换?#
- 自己编写循环
Arrays.asList
将数组转换成ListArrays.stream
将数组转换成流之后再collect
ArrayList.toArray
将List转换成数组
27. ArrayList 和 Vector 的区别是什么?#
见Hashtable
和HashMap
的区别
28. Array 和 ArrayList 有何区别?#
Array这个类??不是反射里面的吗?
如果你是说数组的话,那区别就是,数组可以是基本类型的,并且定长,没有太多方法可以用。ArrayList完全相反。
29. 在 Queue 中 poll()和 remove()有什么区别?#
标准答案
poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。
30. 哪些集合类是线程安全的?#
线程安全是在特定场景下才能讨论的话题,因为不同场景所需要的安全性不同。但以下类可以考虑在多线程环境中使用。
- Vector
- Hashtable
- Stack
此外,如果放宽一些要求,并发包下的一些集合也可以考虑使用:
- ConcurrentHashMap
- ConcurrentSkipList
- CopyOnWriteList
31. 迭代器 Iterator 是什么?#
对序列进行迭代的工具。
32. Iterator 怎么使用?有什么特点?#
标准答案
Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
33. Iterator 和 ListIterator 有什么区别?#
标准答案
- Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
- Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
- ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
作者:Yudoge
出处:https://www.cnblogs.com/lilpig/p/16399988.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
欢迎按协议规定转载,方便的话,发个站内信给我嗷~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)