http://baijiahao.baidu.com/s?id=1638844080997170869&wfr=spider&for=pc
Vector线程安全,ArrayList非线程安全
这篇文章开始介绍Vector。他和ArrayList有一些相似,其内部都是通过一个容量能够动态增长的数组来实现的。不同点是Vector是线程安全的。因为其内部有很多同步代码快来保证线程安全。为此,这篇文章,也会通过从源码的角度来分析一下Vector,并和ArrayList等其他集合容器进行一个对比分析。
OK,开始今天的文章。
一、认识Vector
Vector可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。不过,Vector的大小是可以增加或者减小的,以便适应创建Vector后进行添加或者删除操作。
为此我们先看一下Vector在整个java集合体系中的位置
![](http://pics2.baidu.com/feed/80cb39dbb6fd5266ed9051598250892ed5073694.jpeg?token=74695a9ce2ecde23b765c19c6fa75605&s=6F86EC0E59CE40CA584D3C680200D073)
上面这张图我们也会发现Vector和ArrayList是出于一个等级上面的,继承关系也和ArrayList一样。不过从宏观上只能看到在整个体系中的位置,现在我们从Vector来看看他的继承关系。
![](http://pics5.baidu.com/feed/7a899e510fb30f24b9f17ca5e1ddcf46ac4b03fe.jpeg?token=1ccf9e2e0ff8335351114b236dfe410b&s=23A2536E4FA2B5785469581A0000E0C2)
详细细节
现在我们就根据这张图来进行一个分析,Vector继承于AbstractList,实现了List、RandomAccess、Cloneable、 Serializable等接口。
(1)Vector 继承了AbstractList,实现了List接口。
(2)Vector实现了RandmoAccess接口,即提供了随机访问功能。
(3)Vector 实现了Cloneable接口,即实现克隆功能。
(4)Vector 实现Serializable接口,表示支持序列化。
Vector实现的这些接口,表示会有这样的能力。但是还有一点,就是Vector是线程安全的。下面我们从源码的角度来分析一下Vector是如何实现这些接口和保持线程安全的特性的。
二、源码分析Vector
(1)构造方法
Vector的构造方法一共有四个,因为四个都比较重要,所以在这里就给出四个
第一个: 创建一个空的Vector,并且指定了Vector的初始容量为10
![](http://pics5.baidu.com/feed/ac345982b2b7d0a2fb9b0391e2a7680c4b369a3d.jpeg?token=79f23c180037fd4b962c3a25d8d01191)
第二个:创建一个空的Vector,并且指定了Vector的初始容量
![](http://pics1.baidu.com/feed/d0c8a786c9177f3e3cc9d34b598725c29e3d5647.jpeg?token=2d75db44546882852dd96fdfe1f17ffa)
第三个:创建一个空的Vector,并且指定了Vector的初始容量和扩容时的增长系数
![](http://pics4.baidu.com/feed/37d12f2eb9389b506f0ecbfcad7dfbd8e6116e73.jpeg?token=afa741c8d387671b4c368e4088e98d7d&s=69C08345DFE0B96018CD850D0000F0C3)
第四个:根据其他集合来创建一个非空的Vector
![](http://pics7.baidu.com/feed/e7cd7b899e510fb32d643c83f17bd690d1430c75.jpeg?token=da23d5d052721aeb0ca51b28d877a42e&s=E9C08345CFE49F701C5DE80E000070C1)
第四个需要解释一下,首先是把其他集合转化为数组,然后复制粘贴到Vector里面。
(2)增加元素
增加元素有两个主要的方法,第一个是在Vector尾部追加,第二个是在指定位置插入元素。
第一个:在Vector尾部追加元素
![](http://pics6.baidu.com/feed/f31fbe096b63f624459bdf3eaf0cf5fd1b4ca392.jpeg?token=d88a920a1fb80c17bb9bdd4644d7f70e&s=69D283468EE49D704458800B0000E0C2)
我们再进来看一下ensureCapacityHelper(elementCount + 1)是如何实现的。
![](http://pics3.baidu.com/feed/0bd162d9f2d3572ca47985dea25b7d2263d0c3e2.jpeg?token=63c3cb93b7e1f5f04f15871972f7061f&s=69E08344CDC4BD7006D8900B0000A0C1)
现在相当于真正扩容的方法是grow方法,别着急我们再进来看看。
![](http://pics0.baidu.com/feed/500fd9f9d72a6059b0b7a1f0007c2a9e023bba06.jpeg?token=2d43165a951b1cdb9873382313d94a89&s=E9C0934052F4946C1CC5C40F0000E0C3)
在这一步我们扩容的时候首先就要排除一些异常的情况,首先就是capacityIncrement(需要增加的数量)是否大于0,如果大于0直接增加这么多。然后发现增加了上面那些还不够那就扩充为实际需要minCapacity的大小。最后发现还不够,就只能扩充到虚拟机能表示的数字最大值了。
第二个:在指定位置增加元素
这个就比较简单了。我们直接看源码就能看明白
![](http://pics2.baidu.com/feed/060828381f30e924db96dbd6644070031c95f7f0.jpeg?token=9094437f99ca646f96b69c818bc70c37&s=E9C0834612F4956C56D9FD0D0000E0C3)
(3)删除元素
删除元素时候同样也有两种方法,第一个根据元素值来删除,第二个根据下表来删除元素。
第一个:根据元素值来删除元素
![](http://pics0.baidu.com/feed/8d5494eef01f3a29a63b24a2b06da2345d607cbc.jpeg?token=2ccbde3cae0b3b1033988f39197d99e5)
我们发现删除元素其实是调用了removeElement()方法来删除元素的,没关系不要嫌麻烦,进入这个方法内部看一下。
![](http://pics0.baidu.com/feed/c9fcc3cec3fdfc03c30be303fc779991a5c226e2.jpeg?token=abefa0664e9c6ddb383b7d881717bfdb&s=E9D283460EE08D6C5E70E00B0000E0C3)
到了这一步,我们又发现,执行删除操作的还不是removeElement()方法,而是removeElementAt(i),我们再进入这个方法看看。
![](http://pics0.baidu.com/feed/279759ee3d6d55fb79dd6007446a514f20a4dd41.jpeg?token=2654ddc596b750c16356c04ff710df0c&s=69D083469AAC976C1CDDEC060000E0C3)
到了这个方法我们其实可以分析一下,要删除元素要移动大量的元素,时间效率肯定是不好的。毕竟Vector是通过数组来实现的,而不是通过链表。
第二个:删除指定位置的元素
删除指定位置的元素就比较简单了,我们到指定的位置进行删除就好了,但是同样需要把后面的元素进行移位。
![](http://pics4.baidu.com/feed/14ce36d3d539b60024f4cd6fc1182b2fc75cb7f9.jpeg?token=35fe972d55c37bfc4bd638348ca93b73&s=69C083451AE4A56C1EF9C00F0000F0C3)
(3)更改元素
更改元素我们就先看一个吧。这个在大部分场景下一般不用(大部分,根据自己业务来定)。
![](http://pics7.baidu.com/feed/024f78f0f736afc3257b5c339b51f5c1b7451239.jpeg?token=4119af3ec3139225a7db8c16822d508f&s=69D08346CFA49F700E7DF00B0000E0C1)
(4)查找元素
查找元素我们给出三个,第一个查询Vector容器中是否包含某个元素,第二个查询第一次出现的指定元素的索引,第三个最后一次出现的指定元素的索引。
第一个:查询Vector容器中是否包含某个元素
![](http://pics1.baidu.com/feed/aa64034f78f0f7367a0da24a231dad1ceac41353.jpeg?token=0403b643cb43b54faa2033f5ba33cf2d)
我们发现,查询Vector是否包含某个元素时候,其实是调用了第二个方法,那我们直接就看第二个
第二个:查询第一次出现的指定元素的索引
![](http://pics3.baidu.com/feed/b7003af33a87e950ee3ffb2838704d46fbf2b413.jpeg?token=f1943394b2ea39e683d8d0bae849eaeb&s=A1D083661AE0A56E4C41440F0000E0C2)
第三个:查询最后一次出现的指定元素的索引
![](http://pics2.baidu.com/feed/359b033b5bb5c9eac73208fefc71a8053bf3b3b3.jpeg?token=745204dd04028186da30053b7e9dcfcb&s=29D283461AE4A56C5C41040E0000E0C2)
1)线程安全:
从上面的构造方法还有增删改查的操作其实我们都发现了,都有这么一个synchronized关键字,就是这个关键字为Vector容器提供了一个安全机制,保证了线程安全。
2)构造方法:
Vector实际上是通过一个数组去保存数据的。当我们构造Vecotr时;使用默认构造函数,默认容量大小是10。
3)增加元素:
当Vector容量不足以容纳全部元素时,Vector的容量会增加。若容量增加系数 大于0,则将容量的值增加“容量增加系数”;否则,将容量大小增加一倍。
4)克隆:
Vector的克隆函数,即是将全部元素克隆到一个数组中。
(5)遍历
不过到这可还没结束,还有重要的一点我们还没说,那就是遍历。其实在我之前的文章介绍ArrayList时候已经提到过了,遍历方式也就那么几种,既然Vector是基于数组实现的,那么遍历方式肯定也是随机访问最快。在这里代码演示几个:
![](http://pics0.baidu.com/feed/14ce36d3d539b600b2e8b1d7c3182b2fc65cb77d.jpeg?token=cdde67d9835e6b07620cbe39f4861a43&s=29C08342DAA6F76C5E59440F0000E0C2)
三、Vector与其他容器的区别
源码看完了,对于Vector的实现,我相信你也基本上明白其内部实现了,下面就看看他和别的容器的区别,在文章一开始我们就提到了Vector其实基本上和ArrayList一样的,下面对比分下一下:
ArrayList是线程非安全的,这很明显,因为ArrayList中所有的方法都不是同步的,在并发下一定会出现线程安全问题。另一个方法就是Vector,它是ArrayList的线程安全版本,其实现90%和ArrayList都完全一样,区别在于:
1、Vector是线程安全的,ArrayList是线程非安全的
2、Vector可以指定增长因子,如果该增长因子指定了,那么扩容的时候会每次新的数组大小会在原数组的大小基础上加上增长因子;如果不指定增长因子,那么就给原数组大小*2,源代码是这样的:
![](http://pics6.baidu.com/feed/c8177f3e6709c93d33ab0fabb675e6d9d00054d9.jpeg?token=abc7d62991b04d7fce1e81be8c98468d)
OK,今天的文章就分享到这里,如有问题还请批评指正。