List源码主要讲ArrayList,LinkedList,Vector三个类

1 ArrayList

ArrayList是一个底层基于数组的集合,
首先来看一下它的继承关系,

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable// 版本号private static final long serialVersionUID = 8683452581122892189L;// 缺省容量private static final int DEFAULT_CAPACITY = 10;// 空对象数组private static final Object[] EMPTY_ELEMENTDATA = {};// 缺省空对象数组private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};// 元素数组transient Object[] elementData;// 实际元素大小,默认为0private int size;//集合的最大长度private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

1)构造方法有三种:

1)无参构造

public ArrayList() {// DEFAULTCAPACITY_EMPTY_ELEMENTDATA也是个空的数组this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}

2)有参构造

 public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}

3)参数为一个继承了Collection的类

public ArrayList(Collection<? extends E> c) {elementData = c.toArray();//将给定的集合数组化if ((size = elementData.length) != 0) {// c.toArray might (incorrectly) not return Object[] (see 6260652)if (elementData.getClass() != Object[].class)//判断elementData是不是Object类型elementData = Arrays.copyOf(elementData, size, Object[].class);} else {// replace with empty array.this.elementData = EMPTY_ELEMENTDATA;}}

2)核心方法

a 添加元素

public boolean add(E e) {//判断数组是否会越界,ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;}//用于扩展数组的函数private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private static int calculateCapacity(Object[] elementData, int minCapacity) {//判断数组是否为空,为空的话直接返回默认的10if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}//否则的话返回当前所需长度return minCapacity;}private void ensureExplicitCapacity(int minCapacity) {//增加修改次数modCount++;// overflow-conscious code//如果当前所需长度大于数组容量,给数组扩容if (minCapacity - elementData.length > 0)grow(minCapacity);}private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;//通过位右移将新数组容量扩充为原来的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);}

2 删除元素

 public E remove(int index) {//检查下标是否越界rangeCheck(index);modCount++;E oldValue = elementData(index);int numMoved = size - index - 1;if (numMoved > 0)//判断是否数组中就一个数据System.arraycopy(elementData, index+1, elementData, index,numMoved);//将最后一个元素清空elementData[--size] = null; // clear to let GC do its workreturn oldValue;}

2 Vector

也是基于动态数组来实现的,跟ArrayList很像,但是它是线程安全的,也就是在大部分的方法上都加上了synchronized因此效率较低

3 LinkedList

LinkedList类声明如下:

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable

可以发现 LinkedList继承了 AbstractSequentialList抽象类,而不是像 ArrayList和 Vector那样实现 AbstractList,实际上,java类库中只有 LinkedList继承了这个抽象类,正如其名,它提供了对序列的连续访问的抽象:
LinkedList的底层是 Deque双向链表,实现了 Deque接口,而 Deque接口继承于 Queue接口,因此,在java中,如果要实现队列,一般都使用 LinkedList来实现。
节点内部类

private static class Node<E> {E item; //元素Node<E> next;//下一个节点Node<E> prev;//上一个节点Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}}

集合的元素

//集合元素个数
transient int size = 0;
//头结点
transient Node<E> first;
//尾节点
transient Node<E> last;

构造器

//无参构造器
public LinkedList() {}//传入外部集合的构造器
public LinkedList(Collection<? extends E> c) {this();addAll(c);
}

核心方法:
构造时的addAll方法

 public boolean addAll(int index, Collection<? extends E> c) {checkPositionIndex(index);//判断index是否越界,越界则抛出异常//index就是当前的数组长度Object[] a = c.toArray();//将集合数组化int numNew = a.length;if (numNew == 0)return false;Node<E> pred, succ;//声明pred和succ两个Node对象,用于标识要插入元素的前一个节点和最后一个节点if (index == size) {//如果size等于原数组长度则表示在结尾添加succ = null;pred = last;//last是尾结点} else {succ = node(index);//index位置上的Node对象pred = succ.prev;}for (Object o : a) {//遍历要插入的集合@SuppressWarnings("unchecked") E e = (E) o;Node<E> newNode = new Node<>(pred, e, null);if (pred == null)first = newNode;//如果要插入的位置的前一个节点为null表示是第一个节点,则直接将newNode赋给第一个节点elsepred.next = newNode;//将要插入的集合元素节点对象赋给此位置原节点对象的前一个对象的后一个,即更改前一个节点对象的next指针指到新插入的节点上pred = newNode;//更改指向后将新节点对象赋给pred作为下次循环中新插入节点的前一个对象节点,依次循环}
//此时pred代表集合元素的插入完后的最后一个节点对象if (succ == null) {//结尾添加的话在添加完集合元素后将最后一个集合的节点对象pred作为lastlast = pred;} else {pred.next = succ;//将集合元素的最后一个节点对象的next指针指向原index位置上的Node对象succ.prev = pred;//将原index位置上的pred指针对象指向集合的最后一个对象}size += numNew;modCount++;return true;}

找到index位置的node

  Node<E> node(int index) {// assert isElementIndex(index);if (index < (size >> 1)) {//判断index是否小于size的一半,如果小于则从头遍历节点,否则从结尾遍历节点Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;//从first第一个节点开始,依次将后一个节点赋给xreturn x;//返回index位置上的Node对象,下同理} else {Node<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;}}

当不指定index往LinkedList中添加元素的时候默认是在链表的尾部添加数据

 void linkLast(E e) {//保存链表插入之前的最后一个节点final Node<E> l = last;final Node<E> newNode = new Node<>(l, e, null);last = newNode;if (l == null)//当第一次插入数据的时候,头节点和尾节点指向同一个节点first = newNode;else//插入之前的尾节点的nextNode指向新插入的节点l.next = newNode;//长度加一size++;//修改次数+1modCount++;}

当指定index插入时,会先查询出index位置的元素,然后调用linkBefore将此元素插入到查询到的元素的前一个位置。

public void add(int index, E element) {checkPositionIndex(index);if (index == size)linkLast(element);elselinkBefore(element, node(index));}

linkBefore源码

void linkBefore(E e, Node<E> succ) {// assert succ != null;final Node<E> pred = succ.prev;//新建节点,并将preNode指向idnex-1节点final Node<E> newNode = new Node<>(pred, e, succ);//插入前的index节点的prev指向新节点succ.prev = newNode;if (pred == null)first = newNode;else//index-1节点的nextNode指向新节点pred.next = newNode;size++;modCount++;}

remove方法

public E remove(int index) {//下标越界检查checkElementIndex(index);//unlink 断开指定节点的联系return unlink(node(index));}E unlink(Node<E> x) {//假设x不是null// assert x != null;//定义一个变量element接受x节点中的元素 最后会最后返回值返回final E element = x.item;//定义连个节点分别获得x节点的前后节点引用final Node<E> next = x.next;final Node<E> prev = x.prev;//如果节点前引用为null 说明这是第一个节点if (prev == null) {//x是第一个节点 即将被删除  那么first需要被重新赋值first = next;} else {//如果不是x不是第一个节点  将prev(x的前一个节点)的next指向x的后一个节点(绕过x)prev.next = next;//x的前引用赋值nullx.prev = null;}
//如果节点后引用为null 说明这是最后一个节点  一系列类似前引用的处理方式 不再赘述if (next == null) {last = prev;} else {next.prev = prev;x.next = null;}
//将x节点中的元素赋值nullx.item = null;size--;modCount++;return element;
}

对比ArrayList和LinkedList的区别,LinkedList主要依赖于一个双端对列,在添加和删除元素方面很有优势,但是在查询方面还是ArrayList效率更高

Java集合系列---List源码解析(ArrayList和LinkedList的区别)相关推荐

  1. Java集合系列---Collection源码解析及整体框架结构

    集合的整体框架结构及依赖关系 1.Collection public interface Collection<E> extends Iterable<E> {} Collec ...

  2. Java集合系列---ConcurrentHashMap源码解析

    ConcurrentHashMap是Java并发容器的一员,jdk1.8以后的基本的数据结构和HashMap相似,也是选用了数组+链表/红黑树的结构,在jdk1,.7以前则是采用了分段锁的技术.Con ...

  3. Java集合系列---TreeMap源码解析(巨好懂!!!)

    TreeMap底层是基于红黑树实现,能实现根据key值对节点进行排序,排序可分为自然排序和定制排序. 自然排序:TreeMap的所有key必须实现Comparable接口, 定制排序:创建TreeMa ...

  4. Java集合系列---LinkedHashMap源码解析

    1 首先来看一下LinkedHashMap的继承关系 public class LinkedHashMap<K,V> extends HashMap<K,V> implemen ...

  5. Java集合系列---HashMap源码解析(超详细)

    1 HashMap 1)特性: 底层数据结构是数组+链表+红黑树运行null键和null值,,非线程安全,不保证有序,插入和读取顺序不保证一致,不保证有序,在扩容时,元素的顺序会被重新打乱 实现原理: ...

  6. Java集合之TreeMap源码解析上篇

    上期回顾 上期我从树型结构谈到了红黑树的概念以及自平衡的各种变化(指路上期←戳),本期我将会对TreeMap结合红黑树理论进行解读. 首先,我们先来回忆一下红黑树的5条基本规则. 1.结点是红色或者黑 ...

  7. Java集合部分学习+源码解析

    Java集合 对象的容器,实现了对对象常用的操作,类似数组功能. 集合中的数据都是在内存中,当程序关闭或者重启后集合中的数据就会丢失,所以说是临时存储数据的容器 集合整体框架 Collection:单 ...

  8. Java集合---Arrays类源码解析

    一.Arrays.sort()数组排序 Java Arrays中提供了对所有类型的排序.其中主要分为Primitive(8种基本类型)和Object两大类. 基本类型:采用调优的快速排序: 对象类型: ...

  9. 死磕 java集合之ArrayDeque源码分析

    问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...

最新文章

  1. 两台计算机通过传统电话网络,计算机网络的复习题.doc
  2. 是小厂全栈好,还是大厂专业工程师好?
  3. Android --- 当 item 的数量增加时,ListView 并不能根据所有 item 一共的数量来撑开布局
  4. Spring集成Shiro框架实战
  5. java.lang包—类Class
  6. 科大讯飞语音合成的引入
  7. 天行数据API智能机器人接口
  8. PRKP-1001与CRS-0215解决方案
  9. 酒店服务机器人或被用于长期偷窥旅客
  10. 全国高校计算机能力挑战赛Java试题(一)
  11. vue 定位所在地_Vue 获取当前地理位置
  12. 松弛型内存模型(Relaxed memory models)的一个有趣的例子
  13. Dram学习笔记(1) Dram相关基础知识
  14. 【JZOJ4939】平均值 题解
  15. 人工智能期末考试复习(贲可荣 张彦铎)
  16. 微信公众号获取的实时坐标和高德地图坐标系的转换
  17. MySQL备库复制延迟的原因及解决办法
  18. 网格画法:原生 Canvas 画网格,可拖动、可放大缩小、并带有坐标系 0 0 位置辅助线
  19. Opencv实战 文字区域的提取
  20. 微软黑科技:DNA存储技术催生方糖大小的数据中心

热门文章

  1. [转]配置nginx+apache 其中动态由apache处理,静态由nginx处理
  2. LeetCode 1497. 检查数组对是否可以被 k 整除
  3. Kubernetes滚动更新速率控制解读
  4. 会话信息必须在服务器端,有关会话跟踪技术描述正确的是
  5. 局域网设备与公网服务之间如何交互数据?
  6. 国嵌视频光盘及源码网盘下载地址-续
  7. OSSemPend()--等待一个信号量
  8. php ajax loading图片居中显示,php-通过ajax框架加载漂亮照片
  9. Spring Boot注解详解
  10. 同步工具之Semaphore信号量