Java 中的 List 是非常常用的数据类型。List 是有序的 Collection,Java List 一共有三个实现类,分别是:ArrayList、Vector、LinkedList

本文分析基于 JDK8

ArrayList

ArrayList 继承自 AbstractList,实现了 List 接口。底层基于数组实现容量大小动态变化,初始容量为 10,允许值为 null,有序,非线程安全,擅长随机访问

ArrayList 还实现了 RandomAccess、Cloneable、Serializable 接口,所以 ArrayList 是支持快速访问、复制、序列化的

  • RandomAccess

    标记接口,用来表明其支持快速随机访问。如果是实现了这个接口的 List,那么使用 for 循环的方式获取数据会优于用迭代器获取数据

  • Serializable

    标记该类支持序列化

  • Cloneable

    允许在堆中克隆出一块和原对象一样的对象,并将这个对象的地址赋予新的引用。ArrayList 提供的是一种深克隆机制,即克隆除自身对象以外的所有对象,包括自身所包含的所有对象实例。实现方式是先调用 super.clone() 方法克隆出一个新对象,然后再手动将原数组中的值复制到一个新的数组,并赋值

ArrayList 扩容机制

扩容机制应该是面试中最常问的了。其他关于 ArrayList 的一些琐碎方法我就不细说了,主要介绍一下扩容机制。首先了解一下 ArrayList 的成员属性

  1. // 表示 ArrayList 的默认容量大小
  2. private static final int DEFAULT_CAPACITY = 10;
  3. // 一个空的 Object 数组对象,长度为 0,如果使用默认构造函数创建,则 elementData 默认是该值
  4. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  5. // 最大容量
  6. private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  7. // ArrayList 中存放的实际元素个数
  8. private int size;
  9. // 当前元素对象存放的数组,不参与序列化
  10. transient Object[] elementData;

执行 add 方法时,会先执行 ensureCapacityInternal 方法,判断当前数组容量是否足够,不够就扩容。然后将待添加元素加到 elementData 末尾

  1. public boolean add(E e) {
  2. ensureCapacityInternal(size + 1);
  3. elementData[size++] = e;
  4. return true;
  5. }
  6. private void ensureCapacityInternal(int minCapacity) {
  7. ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
  8. }
  9. private static int calculateCapacity(Object[] elementData, int minCapacity) {
  10. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
  11. return Math.max(DEFAULT_CAPACITY, minCapacity);
  12. }
  13. return minCapacity;
  14. }
  15. private void ensureExplicitCapacity(int minCapacity) {
  16. modCount++;
  17. // minCapacity > elementData.length 则扩容
  18. if (minCapacity - elementData.length > 0)
  19. grow(minCapacity);
  20. }

再分析一下 ensureCapacityInternal 方法,此时 minCapacity 是 size + 1,这里有两个嵌套方法 calculateCapacity 和 ensureExplicitCapacity,作用分别如下:

  • calculateCapacity

    如果当前数组为空,则先设置容量为默认值 10,此时还未初始化数组

  • ensureExplicitCapacity

    确认实际的容量,如果不够就扩容,关键的扩容函数 grow 就在这里

扩展数组大小,首先将容量扩大为原来的 1.5 倍,如果数组是空数组,则将数组初始化,默认容量为 10,如果不是,再判断是否超出最大容量,超过直接赋予最大值,否则赋予新值,复制原数组到新数组

  1. private void grow(int minCapacity) {
  2. // 扩容前的容量
  3. int oldCapacity = elementData.length;
  4. // oldCapacity 右移一位,等于除以二
  5. int newCapacity = oldCapacity + (oldCapacity >> 1);
  6. // 扩容之后还是不够,直接赋予新值
  7. if (newCapacity - minCapacity < 0)
  8. newCapacity = minCapacity;
  9. // 扩容之后超出最大容量,直接赋予最大值
  10. if (newCapacity - MAX_ARRAY_SIZE > 0)
  11. newCapacity = hugeCapacity(minCapacity);
  12. // 复制原数组的值到新数组
  13. elementData = Arrays.copyOf(elementData, newCapacity);
  14. }

LinkedList

LinkedList 继承自 AbstractSequentialList,实现了 List 和 Deque 接口,基于双向链表实现,每个节点都包含了对前一个和后一个元素的引用,可以被当作堆栈、队列或双端队列进行操作,有序,非线程安全

  1. // 指向链表的第一个节点
  2. transient Node<E> first;
  3. // 指向链表的最后一个节点
  4. transient Node<E> last;

JDK8 中 LinkedList 有一个静态内部类 Node,它包括的属性有:当前节点所包含的值,上一个节点,下一个节点

  1. private static class Node<E> {
  2. E item;
  3. Node<E> next;
  4. Node<E> prev;
  5. Node(Node<E> prev, E element, Node<E> next) {
  6. this.item = element;
  7. this.next = next;
  8. this.prev = prev;
  9. }
  10. }

除此之外就没啥好分析的了,LinkedList 不存在容量不足的问题,克隆函数也是将所有元素全都克隆到新的 LinkedList 对象

Vector

Vector 是一个矢量队列,和 ArrayList 类似,继承自 AbstractList,实现了 List 接口,就连额外接口也是一样。不同之处在于:

  • Vector 使用 synchronized 保证线程同步
  • Vector 中遗留了大量传统的方法,这些方法不属于集合框架

Vector 有四个构造方法

  1. // 创建一个默认大小为 10 的向量
  2. public Vector()
  3. // 创建指定大小的向量
  4. public Vector(int initialCapacity)
  5. // 创建指定大小的向量,并且指定增量。增量表示向量每次增加的元素数目
  6. public Vector(int initialCapacity, int capacityIncrement)
  7. // 创建一个包含集合 c 元素的向量
  8. public Vector(Collection<? extends E> c)

Vector 的数据结构和 ArrayList 差不多,它包含了三个成员变量:

  1. // 存放元素的动态数组
  2. protected Object[] elementData;
  3. // 动态数组的实际大小
  4. protected int elementCount;
  5. // 动态数组的增长系数
  6. protected int capacityIncrement;

随着 Vector 中元素的增加,Vector 的容量也会动态增长,capacityIncrement 是与容量增长相关的增长系数,具体增长细节在 grow 函数中,和 ArrayList 类似

  1. private void grow(int minCapacity) {
  2. int oldCapacity = elementData.length;
  3. // 如果 capacityIncrement > 0,新的容量大小 = 旧的容量大小 + 增长系数
  4. // 否则容量扩大为原来的两倍
  5. int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
  6. capacityIncrement : oldCapacity);
  7. if (newCapacity - minCapacity < 0)
  8. newCapacity = minCapacity;
  9. if (newCapacity - MAX_ARRAY_SIZE > 0)
  10. newCapacity = hugeCapacity(minCapacity);
  11. elementData = Arrays.copyOf(elementData, newCapacity);
  12. }

Stack

Stack 是 Vector 的子类,实现了一个标准的后进先出的栈。Stack 也是通过数组实现的,当然了,我们也可以将 LinkedList 当作栈来使用

Stack 只定义了默认构造函数,用来创建一个空栈

  1. public Stack()

Stack 除了具有 Vector 的所有 API,还有自己实现的方法

  1. // 判断堆栈是否为空
  2. public boolean empty()
  3. // 查看堆栈顶部的对象,但不从堆栈中移除它
  4. public synchronized E peek()
  5. // 移除堆栈顶部的对象,并作为此函数的值返回该对象
  6. public synchronized E pop()
  7. // 把对象压入堆栈顶部
  8. public E push(E item)
  9. // 返回对象在堆栈中的位置,以 1 为基数
  10. public synchronized int search(Object o)

Stack 的扩容机制基于 Vector,不过由于没有指定增长系数,所有默认为 0,每次扩容数组长度增大为原来的两倍

Java List 常用集合 ArrayList、LinkedList、Vector的更多相关文章

  1. java类集框架(ArrayList,LinkedList,Vector区别)

    主要分两个接口:collection和Map 主要分三类:集合(set).列表(List).映射(Map)1.集合:没有重复对象,没有特定排序方式2.列表:对象按索引位置排序,可以有重复对象3.映射: ...

  2. Java集合--ArrayList,LinkedList性能分析

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3308900.html 第1部分 List概括 先回顾一下List的框架图 (01) List 是一个接口 ...

  3. Java中List,ArrayList、Vector,map,HashTable,HashMap区别用法

    Java中List,ArrayList.Vector,map,HashTable,HashMap区别用法 标签: vectorhashmaplistjavaiteratorinteger ArrayL ...

  4. ArrayList LinkedList Vector

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

  5. Java容器类List、ArrayList、Vector及map、HashTable、HashMap的区别与用法

    Java容器类List.ArrayList.Vector及map.HashTable.HashMap的区别与用法 ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数 ...

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

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

  7. Java中list集合ArrayList 中contains包含的使用

    Java中list集合ArrayList 中contains包含的使用 https://blog.csdn.net/qq_38556611/article/details/78774690

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

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

  9. ArrayList,LinkedList,Vector集合的认识

    最近在温习Java集合部分,花了三天时间读完了ArrayList与LinkedList以及Vector部分的源码.之前都是停留在简单使用ArrayList的API,读完源码看完不少文章后总算是对原理方 ...

随机推荐

  1. TCL(事务控制语言)

    #TCL/*Transaction Control Language 事务控制语言 事务:一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行. 案例:转账 张三丰 1000 ...

  2. 认证授权:学习OAuth协议

    1.什么是OAuth协议? OAUTH协议为用户资源的授权提供了一个安全的.开放而又简易的标准.同时,任何第三方都可以使用OAuth认证服务,任何服务提供商都可以实现自身的OAuth认证服务,因而OA ...

  3. HTML5移动开发之路(1)——jqMobi中Side Menu实现(类似人人网)

    记得以前在做Native App的时候类似于人人网侧边滑动的效果非常的热,很多app仿照该效果进行开发,在jqMobi中也有类似的效果被称为Side Menu.下面我们来一步一步实现该效果. 首先新建 ...

  4. Apache Tika实战

    Apache Tika实战 Tika 简介 Apache Tika 是一个内容分析工具包,可以检测上千种文件类型,并提取它们的元数据和文本.tika在设计上十分精巧,单一的接口使它易于使用,在搜索引擎 ...

  5. dlopen代码详解——从ELF格式到mmap

    最近一个月的时间大部分在研究glibc中dlopen的代码,基本上对整个流程建立了一个基本的了解.由于网上相关资料比较少,走了不少弯路,故在此记录一二,希望后人能够站在我这个矮子的肩上做出精彩的成果. ...

  6. win10找不到wifi

    禁用->启用  就能用了.

  7. [] !== [] is true

    这周工作看见一个小伙伴给我私信发了这样的一个问题,我深剖了一下,希望大家能早点脱掉这个坑. Question: 如果定义了一个空数组,在开发过程中经常会做这样的一个判断,就是这个数组里发生变化不再是空 ...

  8. iptables实用知识 ,一文学会配置linux防火墙

    目录 1.防火墙的概念 2. linux防火墙 3.linux数据包处理流程 3.1 linux 防火墙将以上流程,固定区分为5个流程节点 3.2 数据流程 4 linux防火墙的实现机制 4.1 i ...

  9. Spring security OAuth2.0认证授权学习第二天(基础概念-授权的数据模型)

    如何进行授权即如何对用户访问资源进行控制,首先需要学习授权相关的数据模型. 授权可简单理解为Who对What(which)进行How操作,包括如下: Who,即主体(Subject),主体一般是指用户 ...

  10. ByteCTF2019

    VIP 第一阶段: 先检查一下程序开的保护: 程序只开了canary和nx保护.接下来用IDA分析反编译出的伪代码 如上图,载edit函数中我们可以控制size的大小,并且程序没有做任何检查,我们再跟 ...