阐述ArrayList、Vector、LinkedList的存储性能和特性

it2023-10-29  76

阐述ArrayList、Vector、LinkedList的存储性能和特性

一个不错的答案

ArrayList和Vector内部都是通过数组实现的,允许快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到更大的连续存储空间中。

由于数组存储空间的连续特性,当从数组的中间位置插入或者删除元素时,需要对数组进行复制和移动,这些操作的代价都是比较高的,因此,数组结构擅长索引数据,不擅长数据的插入和删除。

Vector中的方法使用了synchronized修饰,具有线程安全的特性,ArrayList中的方法没有使用synchronized修饰,不具有线程安全的特性,但也正因如此,性能比Vector高出许多。

LinkedList使用双向链表实现存储(双向链表将内存中零散的内存单元通过引用关联起来,形成一个可以按序索引的线性结构),双向链表不支持随机存取,其索引数据需要进行前向或后向遍历,因此其不擅长索引数据;于此同时,该结构在插入数据时只需要记录本项的前后项即可,因此其擅长数据的插入和删除。

Vector属于遗留容器(Java早期的版本中提供的容器,除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),已经不推荐使用,但是由于ArrayList和LinkedListed都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)。

补充

下面这段代码使用Collections中的synchronizedList方法将非线程安全的容器转换为线程安全的容器。

List<String> list = Collections.synchronizedList(new ArrayList<String>()); list.add("1"); list.add("2"); list.add("3"); synchronized (list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) { //foo(i.next()); System.out.println(i.next()); } }

细心的你一定马上意识到了这段代码一些不同寻常的地方,既然新创建的容器是线程安全的,那为什么在调用其某些方法的时候还需要加锁呢?

再来看一下Collections的synchronizedList方法。

public static <T> List<T> synchronizedList(List<T> list) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : new SynchronizedList<>(list)); }

该方法会根据你传入的list是否实现RandomAccess这个接口来确定是返回SynchronizedRandomAccessList的实现还是SynchronizedList的实现。比如,ArrayList就实现了RandomAccess这个接口,用于表示支持随机存取。LinkedList没有实现RandomAccess这个接口,表示不支持随机存取,只支持通过遍历的方式来进行存取。

其实不论返回的是哪种类型的实现,其本质都是通过包装synchronized关键字到非线程安全的容器方法来将其转化为线程安全的容器。

下面截取了SynchronizedList中的部分代码:

可以看到源码中的add(),remove()等方法包装了synchronized关键字,listIterator(),iterator()等方法没有包装synchronized关键字。

这也就解释了,为什么使用后面两种方法的时候,必须手动加锁(Must be manually synched by user),成功破案!

(完)

最新回复(0)