
Java 面试题解析(容器)
List
、Set
、Map
是否继承自 Collection
接口?
答:List
、Set
是,Map
不是。Map
是键值对映射容器,与 List
和 Set
有明显的区别,而 Set
存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List
是线性结构的容器,适用于按数值索引访问元素的情形。
阐述 ArrayList
、Vector
、LinkedList
的存储性能和特性。
答:
ArrayList
和Vector
都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector
中的方法由于添加了synchronized
修饰,因此Vector
是线程安全的容器,但性能上较ArrayList
差,因此已经是 Java 中的遗留容器。LinkedList
使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。Vector
属于遗留容器(Java 早期的版本中提供的容器,除此之外,Hashtable
、Dictionary
、BitSet
、Stack
、Properties
都是遗留容器),已经不推荐使用,但是由于ArrayList
和LinkedListed
都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections
中的synchronizedList
方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)。
补充:遗留容器中的 Properties
类和 Stack
类在设计上有严重的问题,Properties
是一个键和值都是字符串的特殊的键值对映射,在设计上应该是关联一个 Hashtable
并将其两个泛型参数设置为 String
类型,但是 Java API 中的 Properties
直接继承了 Hashtable
,这很明显是对继承的滥用。这里复用代码的方式应该是 Has-A 关系而不是 Is-A 关系,另一方面容器都属于工具类,继承工具类本身就是一个错误的做法,使用工具类最好的方式是 Has-A 关系(关联)或 Use-A 关系(依赖)。同理,Stack
类继承 Vector
也是不正确的。Sun 公司的工程师们也会犯这种低级错误,让人唏嘘不已。
Collection
和 Collections
的区别?
答:Collection
是一个接口,它是 Set
、List
等容器的父接口;Collections
是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。
List
、Map
、Set
三个接口存取元素时,各有什么特点?
答:List
以特定索引来存取元素,可以有重复元素。Set
不能存放重复元素(用对象的 equals()
方法来区分元素是否重复)。Map
保存键值对(key-value pair)映射,映射关系可以是一对一或多对一。Set
和 Map
容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理论存取时间复杂度为 O(1)
,而基于排序树版本的实现在插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果。
TreeMap
和 TreeSet
在排序时如何比较元素?Collections
工具类中的sort()
方法如何比较元素?
答:TreeSet
要求存放的对象所属的类必须实现 Comparable
接口,该接口提供了比较元素的 compareTo()
方法,当插入元素时会回调该方法比较元素的大小。TreeMap
要求存放的键值对映射的键必须实现 Comparable
接口从而根据键对元素进行排序。Collections
工具类的 sort()
方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现 Comparable
接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是 Comparator
接口的子类型(需要重写 compare
方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java 中对函数式编程的支持)。 例子 1:
1 | public class Student implements Comparable<Student> { |
1 | import java.util.Set; |
例子 2:
1 | public class Student { |
1 | import java.util.ArrayList; |