一:总述:

  主要讲解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. java-mysql(2) Prepared statement

    上一篇学习了java如何链接配置mysql,这篇学习下java如何处理sql预处理语句(PreparedStatement),首先是一个sql预处理的例子: package core; import ...

  2. 使用Arcgis Api for Javascript 调用 本地Portal发布的WebMap

    1.环境搭建 安装Arcgis Portal 10.4,Server 10.4,DataStore ,WebAdaptor for IIS,搭建arcgis api for javascript 4. ...

  3. 【设计模式】行为型01策略模式(strategy patten)

    学设计模式一段时间了,有些懂了,有些半知半解,通过写笔记博客的方式总结一下: 关于策略模式,我的个人理解就是将一些经常变动的算法独立抽取出来,可以是一个方法,也可以是一个策略类,这样,如果有需求变更, ...

  4. Linux就该这么学---第一课 20190705

    要认真学习,认真完成作业,最主要是坚持,我感觉自己坚持这个能力欠缺,要主动改正. 还想说一句,以前我是20期学员,后面没有时间学习了,现在到22期了,我要坚持学习完成,坚持 坚持 坚持!!! 现在分享 ...

  5. 浅入深出Vue:组件

    组件在 vue开发中是必不可少的一环,用好组件这把屠龙刀,就能解决不少问题. 组件是什么 官方的定义: 组件是可复用的 Vue 实例,并且可带有一个名字. 官方的定义已经非常简明了,组件就是一个实例. ...

  6. 查询IP地址的免费API

    1.百度 1.http://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=192.168.0.0&co=&resource_ ...

  7. Jmh测试JDK,CGLIB,JAVASSIST动态代理方式的性能

    前言 JDK,CGLIB,JAVASSIST是常用的动态代理方式. JDK动态代理仅能对具有接口的类进行代理. CGLIB动态代理方式的目标类可以没有接口. Javassist是一个开源的分析.编辑和 ...

  8. HDU 1565:方格取数(1)(最大点权独立集)***

    http://acm.hdu.edu.cn/showproblem.php?pid=1565 题意:中文. 思路:一个棋盘,要使得相邻的点不能同时选,问最大和是多少,这个问题就是最大点权独立集. 可以 ...

  9. jieba GitHUb 结巴分词

    1.GitHub jieba-analysis 结巴分词: https://github.com/fxsjy/jieba 2.jieba-analysis 结巴分词(java版): https://g ...

  10. orm的操作

    ORM的对应关系 ​ 类 —— > 表 ​ 对象 ——> 记录(数据行) ​ 属性 ——> 字段 class Book(model.Model): title=models.Char ...