对比Vector、ArrayList、LinkedList有何区别

我们在学习java基础的时候会接触到集合框架,并且集合框架应用范围也十分广,像我们做后台开发的时候就会经常用到,这篇文章主要讲述集合框架中的list的几种不同的实现方法的区别。

那么问题就来了,Vector、ArrayList、LinkedList究竟有什么区别呢?

首先,这三者都是通过List进行具体实现的,本质上,三者都可以做到对于列表中的某个元素进行增删改,具体功能,也都基本还差不多,但是具体的底层实现上,在行为、性能、线程安全等方面,表现又大不相同。

简介

首先说一下Vector,Vector是java早期提供的动态数组,并且是线程安全的,但是如果不需要线程安全,不建议选择,毕竟同步具有额外开销。Vector内部是使用对象数组来保存数据,可以自动根据需要来扩容,当数组已经满了的时候,就会创建新的数组,并且拷贝原有数组数据。

然后是ArrayList,它也是一种动态数组,但本身不是线程安全的,所以性能就比之前提到的要好很多,这也就是为什么我们平时见到ArrayList会更多的原因了。从功能上来讲,它和Vector近似,ArrayList也是可以根据需要调整容量,不过两者的调整逻辑有所区别,Vector扩容是提高一倍,而ArrayList是提高50%

LinkedList是Java的双向链表,所以它本身不需要像上面两种那样调整容量,同样也不是线程安全的。

应用与分析
  • Vector和ArrayList作为动态数组,其内部元素以数组形式顺序存储的,所以非常适合随机访问的场合。除了尾部插入和删除元素,往往性能会相对较差,因为我们插入元素之后,所有元素都要往后移动一个单位。

  • 而LinkedList进行节点插入、删除却高效得多,但是随机访问性能则要比动态数组慢。

所以我们在实际开发的时候就要考虑具体的业务了,如果业务偏向于插入删除,那么要用什么,如果业务偏向于随机访问,那么要用什么。

我们在认真使用java的集合框架的时候很多方面都需要考虑到位,尤其是以下几点

  • java集合框架的整体设计结构,只要要有大局的整体印象。
  • java提供的主要容器,主要也还是map set list collections等等,了解或者掌握对应的数据结构、算法、思考具体技术选择。
  • 将问题扩展到性能、并发等领域。
  • 集合框架的演进与发展。

在java面试的时候,使用这些集合框架进行一些排序算法也是常见的考点,你至少要知道以下几点:

  1. 内部排序,至少要掌握基础算法如归并,冒泡,快排,选排,插入排序等等。
  2. 外部排序,掌握利用内存和外部存储处理超大数据集,至少要理解过程和思路。

考察算法不仅仅是考你如何进行简单的实现,面试官往往会说用到哪些场景中去实现,并且会问更细致的东西,像复杂度,稳定性(快排,堆排不稳定)等等,在不同的数据集中最好和最差的排序情况,从某个角度如何进一步的优化(比如空间占用,假设业务场景需要最小辅助空间),从简单到复杂,就可以看出面试者的思路。

扩展

首先来理解一下集合框架的整体设计,为了能有一个直观的印象,从极客上摘下了这幅图,并且没有引入线程安全的容器集合类,虽然通常下我们使用map很多,但是他并不是真正的集合。
在这里插入图片描述
我们可以看到java的集合框架,Collection接口是所有集合的根,然后扩展开提供了三大类集合,分别是:

  • List,也就是有序集合,方便增删插
  • Set,Set不允许有重复元素,这是和List相比最明显的区别,也就是不存在两个对象equals返回true。我们在日常开发中有很多需要保证元素唯一性的场合。
  • Queue/Deque,则是 Java提供的标准队列结构的实现,除了集合的基本功能,它还支持类似先入先出或者后入先出等特定行为。因为用到这个通常是并发场景,所以在并发包里。

每种集合的通用逻辑,都被抽象到相应的抽象类中,比如AbstractList就集中了各种List操作的通用部分。这些集合并不孤立,比如LinkedList既是List又是Deque

可以用ide去看看源码,其实TreeSet代码内部就是用TreeMap来实现的,Java类库创建了Dummy对象“PRESENT”作为value,然后所有插入的元素其实是以就像前面提到过的一样,需要向具体的方向进行实现。

以Set的几个实现为例:

  • TreeSet支持自然顺序访问,但是添加,删除,包含等操作的效率很低。

  • HashSet则是用Hash算法,理想情况下,如果哈希散列正常,可以提供常数时间的添加、删除,包含等操作,但是不保证有序。

  • LinkedHashSet,内部构建了一个记录插入顺序的双向链表,因此提供了按照插入顺序遍历的功能,与此同时,也保证了常数时间的添加、删除、包含等操作,这些操作略低于HashSet,因为需要对于链表进行一定的维护。

  • 在遍历元素的时候,HashSet性能受到自身影响,所以初始化的时候,除非有必要,不然不要将HashMap的容量设置过大。而对于LinkedHashSet,由于内部链表提供的方法方便,遍历性能之和元素多少有关。

虽然我们平时都会使用concurrent库来提升cpu的使用率进行一定的并发量的提升,但是以上所提到的List在有些情况下也是可以支持并发的,在Collections内,提供了一套synchronized的方法,比如:

static <T>List<T> synchronizedList(List<T> list)

我们完全可以使用类似方法来实现基本的线程安全集合

List list = Collections.synchronizedList(new Array());

它的实现,基本就是每个方法,比如get set add之类,都通过synchronized添加基本的同步支持,非常简单粗暴,但是也非常实用。注意这些创建的线程安全集合,都符合迭代时候,的fail-fast行为,当意外行为的并发修改时候,尽早抛出ConcurrentModificationException异常,以避免不可预计的行为。

另外一个会被经常问到的问题,就是理解默认的排序算法,具体是怎么排的。

比如说我们要学会区分Arrays.sort()还是Collections.sort()

  • 对于原始数据类型,目前所使用的是所谓双轴快速排序,是一种改进的快速排序的算法。
  • 而对于数据对象而言,目前则是使用TimSort,是一种结合了归并和二分的优化排序算法。TimSort排序是一种思路,就像RPC一样,并不是专属于java的。简单来说思路就是:查找数据已经排好序的分区,进行合并,然后排序。

另外,java8引入了并行排序算法(parallelSort),这是为了充分利用现代多核处理器的计算能力,底层实现基于fork-join框架。

当处理的数据集比较小的时候,差距不明显,甚至还表现差一点;但是,当数据集增长到数万或百万以上时,提高就非常大了,具体还是取决于处理器和系统环境。

排序算法仍然在不断改进,最近双轴快速排序实现的作者提交了一个更进一步的改进,历时多年的研究,目前正在审核和验证阶段。根据作者的性能测试对比,相比于基于归并排序 的实现,新改进可以提高随机数据排序速度提高10%~20%,甚至在其他特征的数据集上也有几倍的提高。
有兴趣也可以不断提高。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值