java基础解析系列(十)---ArrayList和LinkedList源码及使用分析

目录

ArrayList

成员变量

  • 数组元素
  1. 111 private transient Object[] elementData;
  • 数组中元素的个数
  1. 118 private int size;

构造方法

  • 初始化容量为10
  1. Constructs an empty list with an initial capacity of ten.
  2. 137
  3. 138 public ArrayList() {
  4. 139 this(10);
  5. 140 }
  • 可以设置初始容量
  1. 127 public ArrayList(int initialCapacity) {
  2. 128 super();
  3. 129 if (initialCapacity < 0)
  4. 130 throw new IllegalArgumentException("Illegal Capacity: "+
  5. 131 initialCapacity);
  6. 132 this.elementData = new Object[initialCapacity];
  7. 133 }

ensureCapacity方法

  1. 178 public void ensureCapacity(int minCapacity) {
  2. 179 modCount++;
  3. 180 int oldCapacity = elementData.length;
  4. 181 if (minCapacity > oldCapacity) {
  5. 182 Object oldData[] = elementData;
  6. 183 int newCapacity = (oldCapacity * 3)/2 + 1;
  7. 184 if (newCapacity < minCapacity)
  8. 185 newCapacity = minCapacity;
  9. 186 // minCapacity is usually close to size, so this is a win:
  10. 187 elementData = Arrays.copyOf(elementData, newCapacity);
  11. 188 }
  12. 189 }
  • 181行,判断minCapacoty是否大于elementData数组的长度
  • 如果181结果为true,183行设置新的容量为旧的容量*3/2+1
  • 187行进行扩容,创建一个新容量的数组,然后将旧的数组元素复制到新数组中

顺序add方法

  1. 377 public boolean add(E e) {
  2. 378 ensureCapacity(size + 1); // Increments modCount!!
  3. 379 elementData[size++] = e;
  4. 380 return true;
  5. 381 }
  • 378行执行ensureCapacity方法,看插入一个元素后是否需要扩容
  • 379行,将待添加元素放置到下标size的位置,size+1

指定index的add方法

  1. 392 public void add(int index, E element) {
  2. 393 rangeCheckForAdd(index);
  3. 394
  4. 395 ensureCapacity(size+1); // Increments modCount!!
  5. 396 System.arraycopy(elementData, index, elementData, index + 1,
  6. 397 size - index);
  7. 398 elementData[index] = element;
  8. 399 size++;
  9. 400 }
  • 393执行rangeCheckForAdd方法
  1. 577 private void rangeCheckForAdd(int index) {
  2. 578 if (index > size || index < 0)
  3. 579 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
  4. 580 }
  • rangeCheckForAdd方法判断index是否超出范围或者小于0
  • 395执行ensureCapacity方法看是否需要扩容
  • 396行将index开始的元素全部往后移动一位
  • 然后设置index位置的元素为插入元素

remove方法

  1. 411 public E remove(int index) {
  2. 412 rangeCheck(index);
  3. 413
  4. 414 modCount++;
  5. 415 E oldValue = elementData(index);
  6. 416
  7. 417 int numMoved = size - index - 1;
  8. 418 if (numMoved > 0)
  9. 419 System.arraycopy(elementData, index+1, elementData, index,
  10. 420 numMoved);
  11. 421 elementData[--size] = null; // Let gc do its work
  12. 422
  13. 423 return oldValue;
  14. 424 }
  • 412行rangeCheck看传入的index是否在范围之内
  1. 569 private void rangeCheck(int index) {
  2. 570 if (index >= size)
  3. 571 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
  4. 572 }
  • 415通过下标获得该元素
  • 419行进行数组的移动,elementData数组中位置在 index+1 到 index+numMoved-1 之间的组件被分别复制到elementData数组中的 index 到 index+numMoved-1 位置。
  • 把index后面的元素向前移动一位后,将size-1的位置设置为null,方便gc

get方法

  1. 348 public E get(int index) {
  2. 349 rangeCheck(index);
  3. 350
  4. 351 return elementData(index);
  5. 352 }
  • 349行查找index是否超出范围
  • 351直接通过坐标从数组中返回

LinkedList

成员变量

  1. 95 private transient Entry<E> header = new Entry<E>(null, null, null);
  2. 96 private transient int size = 0;
  • 95行head为一个头结点
  • size为链表大小

构造方法

  1. 101 public LinkedList() {
  2. 102 header.next = header.previous = header;
  3. 103 }
  • 102行将header的前节点和后节点设置为header本身,从这里也可以看出这是一个双向链表

add方法

  1. 214 public boolean add(E e) {
  2. 215 addBefore(e, header);
  3. 216 return true;
  4. 217 }
  • 215行执行addBefore方法
  1. 794 private Entry<E> addBefore(E e, Entry<E> entry) {
  2. 795 Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
  3. 796 newEntry.previous.next = newEntry;
  4. 797 newEntry.next.previous = newEntry;
  5. 798 size++;
  6. 799 modCount++;
  7. 800 return newEntry;
  8. 801 }
  • 795行创建一个新的节点
  • 796行和797行修改节点的指针,将新节点放入链表

get方法

  1. 331 public E get(int index) {
  2. 332 return entry(index).element;
  3. 333 }
  • 执行entry方法
  1. 380 private Entry<E> entry(int index) {
  2. 381 if (index < 0 || index >= size)
  3. 382 throw new IndexOutOfBoundsException("Index: "+index+
  4. 383 ", Size: "+size);
  5. 384 Entry<E> e = header;
  6. 385 if (index < (size >> 1)) {
  7. 386 for (int i = 0; i <= index; i++)
  8. 387 e = e.next;
  9. 388 } else {
  10. 389 for (int i = size; i > index; i--)
  11. 390 e = e.previous;
  12. 391 }
  13. 392 return e;
  14. 393 }
  • 从这个方法可以看出并不是就是从前往后一个一个寻找,而是先将size>>1也就是将size除以2,看此时要查看的下标是小于还是大于这个数,如果小于这个数,说明位于链表中点的前面,用next指针寻找,正向寻找,反之用previous指针来寻找,这样可以减少遍历的次数
  • 不过不能像ArrayList通过index直接定位,还是要一个一个寻找

remove方法

  1. 232 public boolean remove(Object o) {
  2. 233 if (o==null) {
  3. 234 for (Entry<E> e = header.next; e != header; e = e.next) {
  4. 235 if (e.element==null) {
  5. 236 remove(e);
  6. 237 return true;
  7. 238 }
  8. 239 }
  9. 240 } else {
  10. 241 for (Entry<E> e = header.next; e != header; e = e.next) {
  11. 242 if (o.equals(e.element)) {
  12. 243 remove(e);
  13. 244 return true;
  14. 245 }
  15. 246 }
  16. 247 }
  17. 248 return false;
  18. 249 }
  • 可以看到,从前往后遍历,找到Object o后执行remove方法
  1. 803 private E remove(Entry<E> e) {
  2. 804 if (e == header)
  3. 805 throw new NoSuchElementException();
  4. 806
  5. 807 E result = e.element;
  6. 808 e.previous.next = e.next;
  7. 809 e.next.previous = e.previous;
  8. 810 e.next = e.previous = null;
  9. 811 e.element = null;
  10. 812 size--;
  11. 813 modCount++;
  12. 814 return result;
  13. 815 }
  • 删除这个节点后,重新调整链表

总结与对比

  • 效率方面,这两个集合对应数据结构中的两个线性表,一个数组一个链表,数组可以通过下标可以快速定位元素,所以自然查找和修改效率高。链表不能快速定位元素,只能一个一个找,所以自然查找效率没有数组快,而链表的优势在于他能快速插入快速删除因为只需修改一下节点的指针就可以,不需要像ArrayList移动元素。
  • 容量方面,因为数组是有容量的,所以当容量不足的时候,需要扩容,扩容后就以为者需要进行一次复制,所以如果使用ArrayList的时候,要初始化一个合适的容量,避免扩容的开销。而链表就没有大小限制,插入一个元素,只要插入一个节点就行了
  • 编程世界里面,同一个问题会有很多的方案,有优点也会一定会有缺点,只是在哪种场景下,优点大于缺点罢了

我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)

作者:jiajun 出处: http://www.cnblogs.com/-new/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。

java基础解析系列(十)---ArrayList和LinkedList源码及使用分析的更多相关文章

  1. Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例

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

  2. java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现

    java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析 ...

  3. java基础解析系列(八)---fail-fast机制及CopyOnWriteArrayList的原理

    fail-fast机制及CopyOnWriteArrayList的原理 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列( ...

  4. java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别

    java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别 目录 java基础解析系列(一)---String.StringBuffer.St ...

  5. java基础解析系列(六)---深入注解原理及使用

    java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer ja ...

  6. java基础解析系列(七)---ThreadLocal原理分析

    java基础解析系列(七)---ThreadLocal原理分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ...

  7. java基础解析系列(九)---String不可变性分析

    java基础解析系列(九)---String不可变性分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---In ...

  8. java基础解析系列(十一)---equals、==和hashcode方法

    java基础解析系列(十一)---equals.==和hashcode方法 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系 ...

  9. java基础解析系列(二)---Integer

    java基础解析系列(二)---Integer 前言:本系列的主题是平时容易疏忽的知识点,只有基础扎实,在编码的时候才能更注重规范和性能,在出现bug的时候,才能处理更加从容. 目录 java基础解析 ...

随机推荐

  1. 关于使用lombok遇到的问题

    在官网上下载了lombok.jar包以后,有两种安装方式 : 1. 双击下载下来的 JAR 包安装 lombok    我选择这种方式安装的时候提示没有发现任何 IDE,所以我没安装成功,我是手动安装 ...

  2. 为什么Java中的String类是不可变的?

    String类是Java中的一个不可变类(immutable class). 简单来说,不可变类就是实例在被创建之后不可修改. 在<Effective Java> Item 15 中提到了 ...

  3. mybatis运行时错误Illegal argument exception argument type mismatch

    使用注解时遇到该错误 使用XML应该也会有相应的错误 解决办法:查看是不是Dao接口的参数列表没有加@Param注解  参数过多时需要该注解指明参数

  4. 分享基于分布式Http长连接框架--设计模型

    追求简单的设计. 也许你的设计功能很强大,但能够在满足你需求的前提下尽量简单明了设计. 当你的设计过于复杂的时候想想是不是有其它路可以走,你站在别人的角度想下,如果别人看了你的设计会不会心领神会,还是 ...

  5. 【机器学习实战】第6章 支持向量机(Support Vector Machine / SVM)

    第6章 支持向量机 <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/lates ...

  6. php用PHPWord库生成word文档的例子

    <?php require_once '../libs/PHPWord/PHPWord/IOFactory.php'; require_once '../../config.php'; $PHP ...

  7. Spring读书笔记——bean加载

    我们的日常开发几乎离不开Spring,他为我们的开发带来了很大的便捷,那么Spring框架是如何做到方便他人的呢.今天就来说说bean如何被加载加载. 我们在xml文件中写过太多类似这样的bean声明 ...

  8. asp.net提高程序性能的技巧(一)

    [摘 要] 我只是提供我几个我认为有助于提高写高性能的asp.net应用程序的技巧,本文提到的提高asp.net性能的技巧只是一个起步,更多的信息请参考<Improving ASP.NET Pe ...

  9. gulp使用1-入门指南

    入门指南 1. 全局安装 gulp: $ npm install --global gulp 或使用cnpm 2. 作为项目的开发依赖(devDependencies)安装: $ npm instal ...

  10. KM算法新识

    看了很多写的好的文章,但是针对代码注释来讲,这篇文章最合适.                                 如果人生会有很长,愿你的荣耀永不散场--wenr大牛. #include ...