底层数据结构
ArrayList和Vector的底层都是采用数组来存储数据,获取数据快而插入数据慢。而且都是通过索引来获取数据。
而这样的设计使得获取数据快,而插入数据慢。
另外在每一次扩容的时候,都要移动数组中的元素。当存储的数据量加大的时候,就会影响读写性能。
LinkedList的底层是采用双向链表来存储数据,插入数据的速度更快。也就是说,它将内存中比较零散的内存单元,通过一个附件的引用关联起来,然后形成一个可以按序号索引的线性结构。这种链式的存储方式和数组的连续存储方式相比的话,它的内存利用率更高一些。LinkedList在索取数据的时候需要根据索引的序号向前或者向后来遍历。但是它插入数据的时候,只需要记录本项的前后项就可以了。所以,LinkedList插入数据的速度要更快一些。
线程安全和性能
其中Vector中的方法都使用了synchronized修饰,因此Vector中对数组的操作都是线程安全的。但是性能上比ArrayList要差一些。
遗留容器
Vector是Java早期的版本中提供的容器,属于遗留容器,官方已经不再推荐使用。
但是由于ArrayList和LinkedList都是非线程安全的,在多线程环境下,我们可以是使用工具类Collections的一个synchronizedList()方法来讲容器转换成线程安全的容器再来使用。这也是利用了装饰器模式的一种应用。
除了Vector之外,还有Hashtable、Dictionary、BitSet、Stack、Properties都是属于遗留容器。
在这些容器中,Properties类存在比较严重的设计缺陷,来看这段源码:
Properties是一个键和值都是字符串的特殊的键值对映射。在设计上应该是关联一个Hashtable,并且将它的两个泛型参数设置为String类型。但是在Java API中,Properties是直接继承了Hashtable,这很明显是对继承的泛滥。主要体现在以下两个方面:
- 首先更具合成复用原则,Properties和Hashtable的代码复用关系应该是Has-A关系,而不是Is-A关系。
- 另一个方面,这两个容器都是属于工具类,继承工具类本身是一个错误的做法。所以使用工具类最好的方式是Has-A关系(关联)或Use-A关系(依赖)。
Stack类在设计上也存在Properties同样的缺陷。
在JDK的Util包中,我们发现Stack类继承了Vector,这个设计也是不太合理的。