要想回答这个问题,可以先把各种都讲特性,然后再从底层存储结构,线程安全,默认大小,扩容机制,迭代器,增删改查效率这几个方向入手。

特性列举

  • ArrayList:动态数组,使用的时候,只需要操作即可,内部已经实现扩容机制。

    • 线程不安全
    • 有顺序,会按照添加进去的顺序排好
    • 基于数组实现,随机访问速度快,插入和删除较慢一点
    • 可以插入null元素,且可以重复
  • Vector和前面说的ArrayList很是类似,这里说的也是1.8版本,它是一个队列,但是本质上底层也是数组实现的。同样继承AbstractList,实现了List,RandomAcess,Cloneable, java.io.Serializable接口。具有以下特点:
    • 提供随机访问的功能:实现RandomAcess接口,这个接口主要是为List提供快速访问的功能,也就是通过元素的索引,可以快速访问到。
    • 可克隆:实现了Cloneable接口
    • 是一个支持新增,删除,修改,查询,遍历等功能。
    • 可序列化和反序列化
    • 容量不够,可以触发自动扩容
    • **最大的特点是:线程安全的*,相当于线程安全的ArrayList
  • LinkedList:链表结构,继承了AbstractSequentialList,实现了List,Queue,Cloneable,Serializable,既可以当成列表使用,也可以当成队列,堆栈使用。主要特点有:
    • 线程不安全,不同步,如果需要同步需要使用List list = Collections.synchronizedList(new LinkedList());
    • 实现List接口,可以对它进行队列操作
    • 实现Queue接口,可以当成堆栈或者双向队列使用
    • 实现Cloneable接口,可以被克隆,浅拷贝
    • 实现Serializable,可以被序列化和反序列化

底层存储结构不同

ArrayListVector底层都是数组结构,而LinkedList在底层是双向链表结构。

线程安全性不同

ArrayList和LinkedList都不是线程安全的,但是Vector是线程安全的,其底层是用了大量的synchronized关键字,效率不是很高。

如果需要ArrayList和LinkedList是线程安全的,可以使用Collections类中的静态方法synchronizedList(),获取线程安全的容器。

默认的大小不同

ArrayList如果我们创建的时候不指定大小,那么就会初始化一个默认大小为10,DEFAULT_CAPACITY就是默认大小。

  1. private static final int DEFAULT_CAPACITY = 10;

Vector也一样,如果我们初始化,不传递容量大小,什么都不指定,默认给的容量是10:

  1. public Vector() {
  2. this(10);
  3. }

而LinkedList底层是链表结构,是不连续的存储空间,没有默认的大小的说法。

扩容机制

ArrayList和Vector底层都是使用数组Object[]来存储,当向集合中添加元素的时候,容量不够了,会触发扩容机制,ArrayList扩容后的容量是按照1.5倍扩容,而Vector默认是扩容2倍。两种扩容都是申请新的数组空间,然后调用数组复制的native函数,将数组复制过去。

Vector可以设置每次扩容的增加容量,但是ArrayList不可以。Vector有一个参数capacityIncrement,如果capacityIncrement大于0,那么扩容后的容量,是以前的容量加上扩展系数,如果扩展系数小于等于0,那么,就是以前的容量的两倍。

迭代器

LinkedList源码中一共定义了三个迭代器:

  • Itr:实现了Iterator接口,是AbstractList.Itr的优化版本。
  • ListItr:继承了Itr,实现了ListIterator,是AbstractList.ListItr优化版本。
  • ArrayListSpliterator:继承于Spliterator,Java 8 新增的迭代器,基于索引,二分的,懒加载器。

VectorArrayList基本差不多,都是定义了三个迭代器:

  • Itr:实现接口Iterator,有简单的功能:判断是否有下一个元素,获取下一个元素,删除,遍历剩下的元素
  • ListItr:继承Itr,实现ListIterator,在Itr的基础上有了更加丰富的功能。
  • VectorSpliterator:可以分割的迭代器,主要是为了分割以适应并行处理。和ArrayList里面的ArrayListSpliterator类似。

LinkedList里面定义了三种迭代器,都是以内部类的方式实现,分别是:

  • ListItr:列表的经典迭代器
  • DescendingIterator:倒序迭代器
  • LLSpliterator:可分割迭代器

增删改查的效率

理论上ArrayListVector检索元素,由于是数组,时间复杂度是O(1),在集合的尾部插入或者删除是O(1),但是其他的地方增加,删除,都是O(n),因为涉及到了数组元素的移动。但是LinkedList不一样,LinkedList不管在任何位置,插入,删除都是O(1)的时间复杂度,但是LinkedList在查找的时候,是O(n)的复杂度,即使底层做了优化,可以从头部/尾部开始索引(根据下标在前一半还是后面一半)。

如果插入删除比较多,那么建议使用LinkedList,但是它并不是线程安全的,如果查找比较多,那么建议使用ArrayList,如果需要线程安全,先考虑使用Collectionsapi获取线程安全的容器,再考虑使用Vector

测试三种结构在头部不断添加元素的结果:


  1. import java.util.ArrayList;
  2. import java.util.LinkedList;
  3. import java.util.List;
  4. import java.util.Vector;
  5. public class Test {
  6. public static void main(String[] args) {
  7. addArrayList();
  8. addLinkedList();
  9. addVector();
  10. }
  11. public static void addArrayList(){
  12. List list = new ArrayList();
  13. long startTime = System.nanoTime();
  14. for(int i=0;i<100000;i++){
  15. list.add(0,i);
  16. }
  17. long endTime = System.nanoTime();
  18. System.out.println((endTime-startTime)/1000/60);
  19. }
  20. public static void addLinkedList(){
  21. List list = new LinkedList();
  22. long startTime = System.nanoTime();
  23. for(int i=0;i<100000;i++){
  24. list.add(0,i);
  25. }
  26. long endTime = System.nanoTime();
  27. System.out.println((endTime-startTime)/1000/60);
  28. }
  29. public static void addVector(){
  30. List list = new Vector();
  31. long startTime = System.nanoTime();
  32. for(int i=0;i<100000;i++){
  33. list.add(0,i);
  34. }
  35. long endTime = System.nanoTime();
  36. System.out.println((endTime-startTime)/1000/60);
  37. }
  38. }

测出来的结果,LinkedList最小,Vector费时最多,基本验证了结果:

  1. ArrayList:7715
  2. LinkedList:111
  3. Vector:8106

测试get的时间性能,往每一个里面初始化10w个数据,然后每次get出来:


  1. import java.util.ArrayList;
  2. import java.util.LinkedList;
  3. import java.util.List;
  4. import java.util.Vector;
  5. public class Test {
  6. public static void main(String[] args) {
  7. getArrayList();
  8. getLinkedList();
  9. getVector();
  10. }
  11. public static void getArrayList(){
  12. List list = new ArrayList();
  13. for(int i=0;i<100000;i++){
  14. list.add(0,i);
  15. }
  16. long startTime = System.nanoTime();
  17. for(int i=0;i<100000;i++){
  18. list.get(i);
  19. }
  20. long endTime = System.nanoTime();
  21. System.out.println((endTime-startTime)/1000/60);
  22. }
  23. public static void getLinkedList(){
  24. List list = new LinkedList();
  25. for(int i=0;i<100000;i++){
  26. list.add(0,i);
  27. }
  28. long startTime = System.nanoTime();
  29. for(int i=0;i<100000;i++){
  30. list.get(i);
  31. }
  32. long endTime = System.nanoTime();
  33. System.out.println((endTime-startTime)/1000/60);
  34. }
  35. public static void getVector(){
  36. List list = new Vector();
  37. for(int i=0;i<100000;i++){
  38. list.add(0,i);
  39. }
  40. long startTime = System.nanoTime();
  41. for(int i=0;i<100000;i++){
  42. list.get(i);
  43. }
  44. long endTime = System.nanoTime();
  45. System.out.println((endTime-startTime)/1000/60);
  46. }
  47. }

测出来的时间如下,LinkedList 执行get操作确实耗时巨大,VectorArrayList在单线程环境其实差不多,多线程环境会比较明显,这里就不测试了:

  1. ArrayList 18
  2. LinkedList 61480
  3. Vector 21

测试删除操作的代码如下,删除的时候我们是不断删除第0个元素:

  1. import java.util.ArrayList;
  2. import java.util.LinkedList;
  3. import java.util.List;
  4. import java.util.Vector;
  5. public class Test {
  6. public static void main(String[] args) {
  7. removeArrayList();
  8. removeLinkedList();
  9. removeVector();
  10. }
  11. public static void removeArrayList(){
  12. List list = new ArrayList();
  13. for(int i=0;i<100000;i++){
  14. list.add(0,i);
  15. }
  16. long startTime = System.nanoTime();
  17. for(int i=0;i<100000;i++){
  18. list.remove(0);
  19. }
  20. long endTime = System.nanoTime();
  21. System.out.println((endTime-startTime)/1000/60);
  22. }
  23. public static void removeLinkedList(){
  24. List list = new LinkedList();
  25. for(int i=0;i<100000;i++){
  26. list.add(0,i);
  27. }
  28. long startTime = System.nanoTime();
  29. for(int i=0;i<100000;i++){
  30. list.remove(0);
  31. }
  32. long endTime = System.nanoTime();
  33. System.out.println((endTime-startTime)/1000/60);
  34. }
  35. public static void removeVector(){
  36. List list = new Vector();
  37. for(int i=0;i<100000;i++){
  38. list.add(i);
  39. }
  40. long startTime = System.nanoTime();
  41. for(int i=0;i<100000;i++){
  42. list.remove(0);
  43. }
  44. long endTime = System.nanoTime();
  45. System.out.println((endTime-startTime)/1000/60);
  46. }
  47. }

测试结果,LinkedList确实效率最高,但是VectorArrayList效率还要高。因为是单线程的环境,没有触发竞争的关系。

  1. ArrayList: 7177
  2. LinkedList: 34
  3. Vector: 6713

下面来测试一下,vector多线程的环境,首先两个线程,每个删除5w元素:

  1. package com.aphysia.offer;
  2. import java.util.ArrayList;
  3. import java.util.LinkedList;
  4. import java.util.List;
  5. import java.util.Vector;
  6. public class Test {
  7. public static void main(String[] args) {
  8. removeVector();
  9. }
  10. public static void removeVector() {
  11. List list = new Vector();
  12. for (int i = 0; i < 100000; i++) {
  13. list.add(i);
  14. }
  15. Thread thread1 = new Thread(new Runnable() {
  16. @Override
  17. public void run() {
  18. for (int i = 0; i < 50000; i++) {
  19. list.remove(0);
  20. }
  21. }
  22. });
  23. Thread thread2 = new Thread(new Runnable() {
  24. @Override
  25. public void run() {
  26. for (int i = 0; i < 50000; i++) {
  27. list.remove(0);
  28. }
  29. }
  30. });
  31. long startTime = System.nanoTime();
  32. thread1.start();
  33. thread2.start();
  34. while (!list.isEmpty()) {
  35. }
  36. long endTime = System.nanoTime();
  37. System.out.println((endTime - startTime) / 1000 / 60);
  38. }
  39. }

测试时间为:12668

如果只使用一个线程,测试的时间是:8216,这也从结果说明了确实Vector在多线程的环境下,会竞争锁,导致执行时间变长。

总结一下

  • ArrayList

    • 底层是数组,扩容就是申请新的数组空间,复制
    • 线程不安全
    • 默认初始化容量是10,扩容是变成之前的1.5倍
    • 查询比较快
  • LinkedList
    • 底层是双向链表,可以往前或者往后遍历
    • 没有扩容的说法,可以当成双向队列使用
    • 增删比较快
    • 查找做了优化,index如果在前面一半,从前面开始遍历,index在后面一半,从后往前遍历。
  • Vector
    • 底层是数组,几乎所有方法都加了Synchronize
    • 线程安全
    • 有个扩容增长系数,如果不设置,默认是增加原来长度的一倍,设置则增长的大小为增长系数的大小。

【刷题笔记】

Github仓库地址:https://github.com/Damaer/codeSolution

笔记地址:https://damaer.github.io/codeSolution/

【作者简介】

秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。个人写作方向:Java源码解析,JDBC,Mybatis,Spring,redis,分布式,剑指Offer,LeetCode等,认真写好每一篇文章,不喜欢标题党,不喜欢花里胡哨,大多写系列文章,不能保证我写的都完全正确,但是我保证所写的均经过实践或者查找资料。遗漏或者错误之处,还望指正。

2020年我写了什么?

开源刷题笔记

平日时间宝贵,只能使用晚上以及周末时间学习写作,关注我,我们一起成长吧~

java集合【12】——— ArrayList,LinkedList,Vector的相同点与区别是什么?的更多相关文章

  1. java 集合之ArrayList、Vector、LinkedList、CopyOnWriteArrayList

    ArrayList 线程不安全. 底层Object[]数组实现,用transient关键字修饰,防止序列化,然后重写了readObject和writeObject方法,为了提高传输效率. 插入时会判断 ...

  2. List集合与Set集合(ArrayList,LinkedList,Vector,HashSet,LinkedHashSet,可变参数)

    List集合介绍及常用方法 import java.util.ArrayList; import java.util.Iterator; import java.util.List; /* java. ...

  3. Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  4. ArrayList LinkedList Vector

    ArrayList是基于数组实现的,没有容量的限制. 在删除元素的时候,并不会减少数组的容量大小,可以调用ArrayList的trimeToSize()来缩小数组的容量. ArrayList, Lin ...

  5. ArrayList, LinkedList, Vector - dudu:史上最详解

    ArrayList, LinkedList, Vector - dudu:史上最详解 我们来比较一下ArrayList, LinkedLIst和Vector它们之间的区别.BZ的JDK版本是1.7.0 ...

  6. java集合系列之LinkedList源码分析

    java集合系列之LinkedList源码分析 LinkedList数据结构简介 LinkedList底层是通过双端双向链表实现的,其基本数据结构如下,每一个节点类为Node对象,每个Node节点包含 ...

  7. Java集合框架之LinkedList浅析

    Java集合框架之LinkedList浅析 一.LinkedList综述: 1.1LinkedList简介 同ArrayList一样,位于java.util包下的LinkedList是Java集合框架 ...

  8. 从源码看Java集合之ArrayList

    Java集合之ArrayList - 吃透增删查改 从源码看初始化以及增删查改,学习ArrayList. 先来看下ArrayList定义的几个属性: private static final int ...

  9. Java集合(六)--ArrayList、LinkedList和Vector对比

    在前两篇博客,学习了ArrayList和LinkedList的源码,地址在这: Java集合(五)--LinkedList源码解读 Java集合(四)--基于JDK1.8的ArrayList源码解读 ...

随机推荐

  1. Oh My Zsh All In One

    Oh My Zsh All In One https://ohmyz.sh/ install # CURL $ sh -c "$(curl -fsSL https://raw.github. ...

  2. node.js 怎么扩大默认的分配的最大运行内存

    node.js 怎么扩大默认的分配的最大运行内存 $ node --max-old-space-size=4096 app.js $ NODE_OPTIONS=--max-old-space-size ...

  3. how to convert a number to a number array in javascript without convert number to a string

    how to convert a number to a number array in javascript without convert number to a string 如何在不将数字转换 ...

  4. npm & app-node-env

    npm & app-node-env $ npm i -g app-node-env # OR $ yarn global add app-node-env demo $ ane env=ap ...

  5. docs search & algolia & docsearch

    docs search & algolia & docsearch https://www.algolia.com/docsearch https://www.algolia.com/ ...

  6. web testing

    web testing cypress https://www.cypress.io/ https://github.com/cypress-io/cypress https://docs.cypre ...

  7. py pandas

    import pandas as pd class Main(): def __init__(self): # 读取excel self.df = pd.read_excel("C:\\Us ...

  8. cocos2dx创建工程

    p.p1 { margin: 0; font: 17px "Helvetica Neue"; color: rgba(69, 69, 69, 1) } 官网镇楼: http://w ...

  9. python使用requests模块下载文件并获取进度提示

    一.概述 使用python3写了一个获取某网站文件的小脚本,使用了requests模块的get方法得到内容,然后通过文件读写的方式保存到硬盘同时需要实现下载进度的显示 二.代码实现 安装模块 pip3 ...

  10. docker mysql初始化多个sql脚本

    一.概述 现有一台服务器,需要部署mysql.其中mysql容器,需要在第一次启动时,执行多个sql文件. 文件名 说明 执行顺序 init.sql 创建数据库以及用户 1 users.sql 用户表 ...