一:总述:

  主要讲解3个集合

  1.ArrayList:

   底层是数组,线程不安全;

  2.LinkedList:

   底层是链表,线程不安全;

  3.Vector

     底层数据结构是数组。线程安全;

二:ArrayList解析

  

  首先,我们来看一下ArrayList的属性:

    /**
    * Default initial capacity.
    */
    private static final int DEFAULT_CAPACITY = 10;//初始化容量值

  1. /**
  2. * Shared empty array instance used for empty instances.
  3. */
  4. private static final Object[] EMPTY_ELEMENTDATA = {};//指定ArrayList的容量为0时,返回该空数组
  5.  
  6. /**
  7. * Shared empty array instance used for default sized empty instances. We
  8. * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
  9. * first element is added.
  10. */
  11. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//与上个属性的区别是:该数组是默认返回的,而上个属性是指定容量为0时返回
  12.  
  13. /**
  14. * The array buffer into which the elements of the ArrayList are stored.
  15. * The capacity of the ArrayList is the length of this array buffer. Any
  16. * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
  17. * will be expanded to DEFAULT_CAPACITY when the first element is added.
  18. */
  19. transient Object[] elementData; // non-private to simplify nested class access//第一次保存元素时,数组将会扩容
  20.  
  21. /**
  22. * The size of the ArrayList (the number of elements it contains).
  23. *
  24. * @serial
  25. */
  26. private int size;//ArrayList的实际大小

  根据上面我们可以清晰的发现:ArrayList底层其实就是一个数组,ArrayList中有扩容这么一个概念,正因为它扩容,所以它能够实现“动态”增长

2.2构造方法

  1. /**
  2. * Constructs an empty list with the specified initial capacity.
  3. *
  4. * @param initialCapacity the initial capacity of the list
  5. * @throws IllegalArgumentException if the specified initial capacity
  6. * is negative
  7. */
      //指定初始化长度initCapacity
  8. public ArrayList(int initialCapacity) {
  9. if (initialCapacity > 0) {
  10. this.elementData = new Object[initialCapacity];
  11. } else if (initialCapacity == 0) {
  12. this.elementData = EMPTY_ELEMENTDATA;
  13. } else {
  14. throw new IllegalArgumentException("Illegal Capacity: "+
  15. initialCapacity);
  16. }
  17. }
  18.  
  19. /**
  20. * Constructs an empty list with an initial capacity of ten.
  21. */
      //否则返回的是:DEFAULTCAPACITY_EMPTY_ELEMENTDATA
  22. public ArrayList() {
  23. this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  24. }
  25.  
  26. /**
  27. * Constructs a list containing the elements of the specified
  28. * collection, in the order they are returned by the collection's
  29. * iterator.
  30. *
  31. * @param c the collection whose elements are to be placed into this list
  32. * @throws NullPointerException if the specified collection is null
  33. */
  34. public ArrayList(Collection<? extends E> c) {
  35. elementData = c.toArray();
  36. if ((size = elementData.length) != 0) {
  37. // c.toArray might (incorrectly) not return Object[] (see 6260652)
  38. if (elementData.getClass() != Object[].class)
  39. elementData = Arrays.copyOf(elementData, size, Object[].class);
  40. } else {
  41. // replace with empty array.
  42. this.elementData = EMPTY_ELEMENTDATA;
  43. }
  44. }

2.3 Add()方法

源码如下:

  1. /**
  2. * Appends the specified element to the end of this list.
  3. *
  4. * @param e element to be appended to this list
  5. * @return <tt>true</tt> (as specified by {@link Collection#add})
  6. */
  7. public boolean add(E e) {
  8. ensureCapacityInternal(size + 1); // Increments modCount!!
  9. elementData[size++] = e;
  10. return true;
  11. }
  12.  
  13. /**
  14. * Inserts the specified element at the specified position in this
  15. * list. Shifts the element currently at that position (if any) and
  16. * any subsequent elements to the right (adds one to their indices).
  17. *
  18. * @param index index at which the specified element is to be inserted
  19. * @param element element to be inserted
  20. * @throws IndexOutOfBoundsException {@inheritDoc}
  21. */
  22. public void add(int index, E element) {
  23. rangeCheckForAdd(index);
  24.  
  25. ensureCapacityInternal(size + 1); // Increments modCount!!
  26. System.arraycopy(elementData, index, elementData, index + 1,
  27. size - index);
  28. elementData[index] = element;
  29. size++;
  30. }

2.3.1 Add(E e)

步骤:

  • 检查是否需要扩容
  • 插入元素

首先,我们来看看这个方法:

  1. public boolean add(E e) {
  2. ensureCapacityInternal(size + 1); // Increments modCount!!
  3. elementData[size++] = e;
  4. return true;
  5. }

该方法很短,我们可以根据方法名就猜到他是干了什么:

  • 确认list容量,尝试容量加1,看看有无必要
  • 添加元素

接下来我们来看看这个小容量(+1)是否满足我们的需求:

  1. private void ensureCapacityInternal(int minCapacity) {
         //想要得到的最小的容量
  2. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
  3. minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
  4. }
  5.      //确定明确的容量
  6. ensureExplicitCapacity(minCapacity);
  7. }
  1. private void ensureExplicitCapacity(int minCapacity) {
  2. modCount++;
  3.      //如果最小容量比数组长度大,则用用grow扩容
  4. // overflow-conscious code
  5. if (minCapacity - elementData.length > )
  6. grow(minCapacity);
  7. }

接下来看grow是如何扩容的

  1. /**
  2. * Increases the capacity to ensure that it can hold at least the
  3. * number of elements specified by the minimum capacity argument.
  4. *
  5. * @param minCapacity the desired minimum capacity
  6. */
  7. private void grow(int minCapacity) {
  8. // overflow-conscious code
  9. int oldCapacity = elementData.length;
  10. int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容1.5倍
  11. if (newCapacity - minCapacity < 0)
  12. newCapacity = minCapacity;
  13. if (newCapacity - MAX_ARRAY_SIZE > 0)
  14. newCapacity = hugeCapacity(minCapacity);
  15. // minCapacity is usually close to size, so this is a win:
  16. elementData = Arrays.copyOf(elementData, newCapacity);//扩容完后调用copyOf方法把原数组的值存入新数组
  17. }

再来看是怎么把原数组的值放入新数组

  1. /**
  2. * Copies the specified array, truncating or padding with nulls (if necessary)
  3. * so the copy has the specified length. For all indices that are
  4. * valid in both the original array and the copy, the two arrays will
  5. * contain identical values. For any indices that are valid in the
  6. * copy but not the original, the copy will contain <tt>null</tt>.
  7. * Such indices will exist if and only if the specified length
  8. * is greater than that of the original array.
  9. * The resulting array is of the class <tt>newType</tt>.
  10. *
  11. * @param <U> the class of the objects in the original array
  12. * @param <T> the class of the objects in the returned array
  13. * @param original the array to be copied
  14. * @param newLength the length of the copy to be returned
  15. * @param newType the class of the copy to be returned
  16. * @return a copy of the original array, truncated or padded with nulls
  17. * to obtain the specified length
  18. * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
  19. * @throws NullPointerException if <tt>original</tt> is null
  20. * @throws ArrayStoreException if an element copied from
  21. * <tt>original</tt> is not of a runtime type that can be stored in
  22. * an array of class <tt>newType</tt>
  23. * @since 1.6
  24. */
  25. public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
  26. @SuppressWarnings("unchecked")
  27. T[] copy = ((Object)newType == (Object)Object[].class)
  28. ? (T[]) new Object[newLength]
  29. : (T[]) Array.newInstance(newType.getComponentType(), newLength);
  30. System.arraycopy(original, 0, copy, 0,
  31. Math.min(original.length, newLength));
  32. return copy;
  33. }

到目前为止,我们就可以知道add(E e)的基本实现了:

  • 首先去检查一下数组的容量是否足够

    • 足够:直接添加
    • 不足够:扩容
      • 扩容到原来的1.5倍
      • 第一次扩容后,如果容量还是小于minCapacity,就将容量扩充为minCapacity。

2.3.2:add(int index, E element)

步骤:

  • 检查角标
  • 空间检查,如果有需要进行扩容
  • 插入元素

我们来看看插入的实现:

  1. /**
  2. * Inserts the specified element at the specified position in this
  3. * list. Shifts the element currently at that position (if any) and
  4. * any subsequent elements to the right (adds one to their indices).
  5. *
  6. * @param index index at which the specified element is to be inserted
  7. * @param element element to be inserted
  8. * @throws IndexOutOfBoundsException {@inheritDoc}
  9. */
  10. public void add(int index, E element) {
  11. rangeCheckForAdd(index);//检查是否越界
  12.  
  13. ensureCapacityInternal(size + 1); // Increments modCount!!//扩容
  14. System.arraycopy(elementData, index, elementData, index + 1,
  15. size - index);//调用arraycopy进行插入
  16. elementData[index] = element;
  17. size++;
  18. }

注:arraycopy是用c++来编写的

2.4:get()

  • 检查角标
  • 返回元素
  1. /**
  2. * Returns the element at the specified position in this list.
  3. *
  4. * @param index index of the element to return
  5. * @return the element at the specified position in this list
  6. * @throws IndexOutOfBoundsException {@inheritDoc}
  7. */
  8. public E get(int index) {
  9. rangeCheck(index);
  10.  
  11. return elementData(index);
  12. }
  1. // 检查角标
  2. private void rangeCheck(int index) {
  3. if (index >= size)
  4. throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
  5. }
  6.  
  7. // 返回元素
  8. E elementData(int index) {
  9. return (E) elementData[index];
  10. }

2.5:set()方法

步骤:

  • 检查角标
  • 替代元素
  • 返回旧值
  1. /**
  2. * Replaces the element at the specified position in this list with
  3. * the specified element.
  4. *
  5. * @param index index of the element to replace
  6. * @param element element to be stored at the specified position
  7. * @return the element previously at the specified position
  8. * @throws IndexOutOfBoundsException {@inheritDoc}
  9. */
  10. public E set(int index, E element) {
  11. rangeCheck(index);
  12.      //将值进行替代,返回旧值
  13. E oldValue = elementData(index);
  14. elementData[index] = element;
  15. return oldValue;
  16. }

2.6:remove()方法

步骤:

  • 检查角标
  • 删除元素
  • 计算出需要移动的个数,并移动
  • 设置为null,让Gc回收
  1. /**
  2. * Removes the element at the specified position in this list.
  3. * Shifts any subsequent elements to the left (subtracts one from their
  4. * indices).
  5. *
  6. * @param index the index of the element to be removed
  7. * @return the element that was removed from the list
  8. * @throws IndexOutOfBoundsException {@inheritDoc}
  9. */
  10. public E remove(int index) {
  11. rangeCheck(index);
  12.  
  13. modCount++;
  14. E oldValue = elementData(index);
  15.     
         //左移的个数
  16. int numMoved = size - index - 1;
  17. if (numMoved > 0)
  18. System.arraycopy(elementData, index+1, elementData, index,
  19. numMoved);
  20. elementData[--size] = null; // clear to let GC do its work
  21.  
  22. return oldValue;
  23. }

2.7:总述

  • ArrayList是基于动态数组实现的,在增删时候,需要数组的拷贝复制(使用的是System.arrayCopy()效率最高的数组拷贝方法)
  • ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的一半,也就是变为原来的1.5倍
  • 删除元素时不会减少容量,若希望减少容量则调用trimToSize()
  • 它不是线程安全的。它能存放null值。

三:Vector与ArrayList的区别

1.Vector底层也是数组,与ArrayList最大的区别就是:同步(线程安全),Vector的每个方法都是同步的 (相对效率较低)

2.在要求非同步的情况下,我们一般都是使用ArrayList来替代Vector的了,如果想要ArrayList实现同步,可以使用Collections的方法:List list =Collections.synchronizedList(new ArrayList(...));,就可以实现同步了

3.ArrayList是以1.5倍扩容,Vector是以2倍扩容

以上的结论可以在源码中得到验证

四:LinkedList解析

此处放一张全家桶

LinkedList底层是双向链表

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

  4.1:构造方法

  1. /**
  2. * Constructs an empty list.
  3. */
  4. public LinkedList() {
  5. }
  6.  
  7. /**
  8. * Constructs a list containing the elements of the specified
  9. * collection, in the order they are returned by the collection's
  10. * iterator.
  11. *
  12. * @param c the collection whose elements are to be placed into this list
  13. * @throws NullPointerException if the specified collection is null
  14. */
  15. public LinkedList(Collection<? extends E> c) {
  16. this();
  17. addAll(c);
  18. }

4.2: add()方法

  1.   public boolean add(E e) {
  2. linkLast(e);
  3. return true;
  4. }
  5.   //往链表的最后添加元素
  6. void linkLast(E e) {
  7. final Node<E> l = last;
  8. final Node<E> newNode = new Node<>(l, e, null);
  9. last = newNode;
  10. if (l == null)
  11. first = newNode;
  12. else
  13. l.next = newNode;
  14. size++;
  15. modCount++;
  16. }

4.3:remove()方法

  1. /**
  2. * Removes the first occurrence of the specified element from this list,
  3. * if it is present. If this list does not contain the element, it is
  4. * unchanged. More formally, removes the element with the lowest index
  5. * {@code i} such that
  6. * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
  7. * (if such an element exists). Returns {@code true} if this list
  8. * contained the specified element (or equivalently, if this list
  9. * changed as a result of the call).
  10. *
  11. * @param o element to be removed from this list, if present
  12. * @return {@code true} if this list contained the specified element
  13. */
  14. public boolean remove(Object o) {
  15. if (o == null) {
  16. for (Node<E> x = first; x != null; x = x.next) {
  17. if (x.item == null) {
                //删除元素
  18. unlink(x);
  19. return true;
  20. }
  21. }
  22. } else {
  23. for (Node<E> x = first; x != null; x = x.next) {
             //判断元素是否存在里面
  24. if (o.equals(x.item)) {
  25. unlink(x);
  26. return true;
  27. }
  28. }
  29. }
  30. return false;
  31. }
  1. /**
  2. * Unlinks non-null node x.
  3. */
  4. E unlink(Node<E> x) {
  5. // assert x != null;
  6. final E element = x.item;
  7. final Node<E> next = x.next;
  8. final Node<E> prev = x.prev;
  9.  
  10. if (prev == null) {
  11. first = next;
  12. } else {
  13. prev.next = next;
  14. x.prev = null;
  15. }
  16.  
  17. if (next == null) {
  18. last = prev;
  19. } else {
  20. next.prev = prev;
  21. x.next = null;
  22. }
  23.  
  24. x.item = null;
  25. size--;
  26. modCount++;
  27. return element;
  28. }

4.4:get()方法

  1. public E get(int index) {
  2. checkElementIndex(index);
  3. return node(index).item;
  4. }

node()方法

  1. /**
  2. * Returns the (non-null) Node at the specified element index.
  3. */
  4. Node<E> node(int index) {
  5. // assert isElementIndex(index);
  6.      //下标小于长度的一半,从头部开始遍历
  7. if (index < (size >> 1)) {
  8. Node<E> x = first;
  9. for (int i = 0; i < index; i++)
  10. x = x.next;
  11. return x;
         //否则从尾部开始遍历
  12. } else {
  13. Node<E> x = last;
  14. for (int i = size - 1; i > index; i--)
  15. x = x.prev;
  16. return x;
  17. }
  18. }

4.5:set方法

set方法和get方法其实差不多,根据下标来判断是从头遍历还是从尾遍历

  1. public E set(int index, E element) {
  2. checkElementIndex(index);
  3. Node<E> x = node(index);
  4. E oldVal = x.item;
  5. x.item = element;
  6. return oldVal;
  7. }

具体请参考源码

五:总结

ArrayList:

  • 底层实现是数组
  • ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的一半,也就是变为原来的1.5倍
  • 在增删时候,需要数组的拷贝复制(C++实现)

LinkedList:

  • 底层实现是双向链表[双向链表方便实现往前遍历]

Vector:

  • 底层是数组,现在已少用,被ArrayList替代,原因有两个:

    • Vector所有方法都是同步,有性能损失
    • Vector初始length是10 超过length时 以100%比率增长,相比于ArrayList更多消耗内存

总的来说:查询多用ArrayList,增删多用LinkedList。

ArrayList增删慢不是绝对的(在数量大的情况下,已测试):

  • 如果增加元素一直是使用add()(增加到末尾)的话,那是ArrayList要快
  • 一直删除末尾的元素也是ArrayList要快【不用复制移动位置】
  • 至于如果删除的是中间的位置的话,还是ArrayList要快

但一般来说:增删多还是用LinkedList,因为上面的情况是极端的~

List集合源码解读的更多相关文章

  1. 【Java集合】ArrayDeque源码解读

    简介 双端队列是一种特殊的队列,它的两端都可以进出元素,故而得名双端队列. ArrayDeque是一种以循环数组方式实现的双端队列,它是非线程安全的. 它既可以作为队列也可以作为栈. 继承体系 Arr ...

  2. SDWebImage源码解读之SDWebImageCache(上)

    第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...

  3. AFNetworking 3.0 源码解读 总结(干货)(下)

    承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...

  4. AFNetworking 3.0 源码解读 总结(干货)(上)

    养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...

  5. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager

    做ios开发,AFNetworking 这个网络框架肯定都非常熟悉,也许我们平时只使用了它的部分功能,而且我们对它的实现原理并不是很清楚,就好像总是有一团迷雾在眼前一样. 接下来我们就非常详细的来读一 ...

  6. AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization

    这篇就讲到了跟请求相关的类了 关于AFNetworking 3.0 源码解读 的文章篇幅都会很长,因为不仅仅要把代码进行详细的的解释,还会大概讲解和代码相关的知识点. 上半篇: URI编码的知识 关于 ...

  7. AFNetworking 3.0 源码解读(四)之 AFURLResponseSerialization

    本篇是AFNetworking 3.0 源码解读的第四篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3 ...

  8. AFNetworking 3.0 源码解读(五)之 AFURLSessionManager

    本篇是AFNetworking 3.0 源码解读的第五篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3 ...

  9. AFNetworking 3.0 源码解读 总结

    终于写完了 AFNetworking 的源码解读.这一过程耗时数天.当我回过头又重头到尾的读了一篇,又有所收获.不禁让我想起了当初上学时的种种情景.我们应该对知识进行反复的记忆和理解.下边是我总结的 ...

随机推荐

  1. python连接数据库(1)——mysql

    mysql是世界上应用最广的免费数据库,python当然也提供了对它的调用. 首先pip install pymysql,当然自己要知道数据库的用户名和密码,本地数据库的host就是localhost ...

  2. 【web自动化测试】requests-html 这个解析库,能让你更轻松的获取网页内容

    1. 开始 Python 中可以进行网页解析的库有很多,常见的有 BeautifulSoup 和 lxml 等.在网上玩爬虫的文章通常都是介绍 BeautifulSoup 这个库,我平常也是常用这个库 ...

  3. iOS开发(4):录音AVAudioRecorder

    录音,声音的采集,一般有两种实现办法,一是使用AVAudioRecorder,一是使用AudioUnit.如果只是简单的录音,使用AVAudioRecorder就可以了,如果想更灵活地处理刚录到的声音 ...

  4. 《周四橄榄球之夜》流媒体视频拆解:Twitch VS Amazon Prime

    文 / Phil Cluff 译 / 王月美 原文链接:https://mux.com/blog/thursday-night-football-streaming-technology-showdo ...

  5. spring 5.x 系列第12篇 —— 整合memcached (代码配置方式)

    文章目录 一.说明 1.1 XMemcached客户端说明 1.2 项目结构说明 1.3 依赖说明 二.spring 整合 memcached 2.1 单机配置 2.2 集群配置 2.3 存储基本类型 ...

  6. 在java项目启动时就执行某操作

    在java启动时大概有四种,此处只介绍3种 1.在启动的方法上使用通过@PostConstruct方法实现初始化bean进行操作 2.通过bean实现InitializingBean接口 @Overr ...

  7. vscode解决同步设置插件连接不上git

    vscode有一款比较好用的插件,就是设置同步.可以在一台电脑上同步另一台电脑的所有配置及插件! Settings Sync 但是在公司电脑有个奇葩问题,就是连接不上git. 解决:配置代理 &quo ...

  8. 网络学习笔记(三):HTTP缓存

      HTTP缓存是一种保存资源副本并在下次请求时直接使用该副本的技术,合理的使用缓存可以有效的提升web性能.   浏览器将js文件.css文件.图片等资源缓存,当下次请求这些资源时,可以不发送网络请 ...

  9. java中方法的重载和覆盖

    java中方法的重载和覆盖 先来了解一下这两个名词的含义. 重载: 在一个类当中才可以重载,方法名相同,参数个数不同或参数个数相同而参数类型不同. 覆盖: 又称重写,在派生类(子类)中重写基类(父类) ...

  10. ASP.NET Core on K8S学习初探(3)部署API到K8S

    在上一篇<基本概念快速一览>中,我们把基本的一些概念快速地简单地不求甚解地过了一下,本篇开始我们会将ASP.NET Core WebAPI部署到K8S,从而结束初探的旅程. Section ...