博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ArrayList和LinkedList 内部结构分析(一)
阅读量:6829 次
发布时间:2019-06-26

本文共 5284 字,大约阅读时间需要 17 分钟。

   在了解集合的时候,都会学到不同集合之间的区别,比如ArrayList和LinkedList,其中ArrayList是类似于数组结构的,查询比较快速。而LinkedList则是链表结构,在插入和删除的时候效率较高。

  通过研究源码,可以更深入的了解其内部实现,真的是ArrayList所有查询都快么?  真的是 LinkedList所有的插入和删除都更效率么?

  废话不多说,上代码!

  一、先摘抄一些ArrayList的代码吧,这里只分析一些关键的方法:

1、ArrayList的内部结构是怎么样的?

1public class ArrayList
extends AbstractList
implements List
, RandomAccess, Cloneable, java.io.Serializable{ private static final Object[] EMPTY_ELEMENTDATA = {}; //这就是ArrayList集合内部结构,是一个object数组 private transient Object[] elementData;   //集合里面实际有值的长度 private int size;  //构造函数,入参为初始化list长度 public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; }   //默认构造函数,初始化数组大小为0 public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; }   //入参为集合将集合类型数据为数组,list长度为数组长度 public ArrayList(Collection
c) { elementData = c.toArray(); size = elementData.length; // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); }}

2、对ArrayList中元素进行操作的实现?

get(index):其实就是根据index,直接获取对应数组下角标的值

\\根据下角标获取元素public E get(int index) {    \\检查下角标范围        rangeCheck(index);      \\根据下角标返回数组元素        return elementData(index);    }\\下角标范围必须小于数组有值的实际长度private void rangeCheck(int index) {        if (index >= size)            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));    }
\\获取下角标元素 E elementData(int index) {
return (E) elementData[index]; }

 

set(int index, E element):这里需要稍微注意一下的是,set()方法返回的结果是对应index值的原始值。
public E set(int index, E element) {       \\检查下角标        rangeCheck(index);        \\原index 对应的值        E oldValue = elementData(index);       \\设置新值        elementData[index] = element;      \\返回原值        return oldValue;    }

 

add(e):当添加一个元素时候,默认添加到list最后,并且对容器大小是否够容纳进行check,如果容器大小不够,则将原来容器

增大到1.5倍,在这种情况还不够的情况,则增加到最小要求容器长度。

\\添加一个元素public boolean add(E e) {      \\确保当前elementData容量是否足够        ensureCapacityInternal(size + 1);  // Increments modCount!!        elementData[size++] = e;        return true;    }\\确保当前elementData容量是否足够,入参:最小需要长度private void ensureCapacityInternal(int minCapacity) {              if (elementData == EMPTY_ELEMENTDATA) {           \\如果elementData为空,则选择默认长度DEFAULT_CAPACITY或者最小长度            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);        }        //        ensureExplicitCapacity(minCapacity);    }private void ensureExplicitCapacity(int minCapacity) {       //记录集合修改次数        modCount++;       //如果现有容器长度不够用则添加容器长度        if (minCapacity - elementData.length > 0)            grow(minCapacity);    }private void grow(int minCapacity) {        \\原始容器长度        int oldCapacity = elementData.length;       \\新容器长度为原来长度的1.5倍        int newCapacity = oldCapacity + (oldCapacity >> 1);       \\1.5倍的容器长度还比最小要求长度小        if (newCapacity - minCapacity < 0)            newCapacity = minCapacity;        \\判断新容器长度超过最大限制        if (newCapacity - MAX_ARRAY_SIZE > 0)            newCapacity = hugeCapacity(minCapacity);                //将原来数组的值copy新数组中去, ArrayList的引用指向新数组     //这儿会新创建数组,如果数据量很大,重复的创建的数组,那么还是会影   响效率,        elementData = Arrays.copyOf(elementData, newCapacity);    }

在最后需要通过Arrays.copyOf(elementData, newCapacity),复制整个数组的方式来实现添加数据,由此看来add(e)在需要扩容的时候添加元素,

效率是比较低的。

 

add(int index, E element):在指定位置后插入元素
public void add(int index, E element) {        rangeCheckForAdd(index);        // 如果数组长度不足,将进行扩容。        ensureCapacityInternal(size + 1);  // Increments modCount!!       //将当前位于该位置的元素以及所有后续元素右移一个位置。        System.arraycopy(elementData, index, elementData, index + 1,                         size - index);        elementData[index] = element;        size++;    }

可以看到,在指定位置插入元素,首先需要进行容器长度check,如果不足,会进行一个数组copy,接下来还

将当前位于该位置的元素以及所有后续元素右移一个位置。效率更加低了!
remove(int index):
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 work        return oldValue;    }

首先是检查范围,修改modCount,保留将要被移除的元素,将移除位置之后的元素向前挪动一个位置,将list末尾元素置空(null),返回被移除的元素,

移除一个元素,除了最后一位,别的都需要将之后的元素前移一位,可见效率很低!

 

contains(o):直接通过遍历数组,来判断,说明遍历查找效率较高

//是否包含某元素,遍历整个list public boolean contains(Object o) {        return indexOf(o) >= 0;    }public int indexOf(Object o) {      //直接通过遍历数组,来判断是否存在元素,效率较高        if (o == null) {            for (int i = 0; i < size; i++)                if (elementData[i]==null)                    return i;        } else {            for (int i = 0; i < size; i++)                if (o.equals(elementData[i]))                    return i;        }        return -1;    }

 

 

 

 

------------------------------------

上面的源码能看出来,对于ArrayList是如何动态的控制长度,在插入元素、新增元素、删除元素的时候,效率是比较低的。而遍历查找元素,效率较高!

 

 

LinkedList,见下一篇吧,太困了!

 

23:57:43

2016/03/23  夜  杭州

 

 

 

转载于:https://www.cnblogs.com/guoliangxie/p/5306318.html

你可能感兴趣的文章
JS封装动画框架,网易轮播图,旋转轮播图
查看>>
前端项目微金所1 - bootstrap模板,Compatible(兼容),Viewport(视口),条件注释,第三方依赖,MediaQuery媒体查询...
查看>>
【java】java学习之路-06-MySQL(四)
查看>>
解决myeclipse部署web项目时报错
查看>>
使用REGINI修改注册表权限
查看>>
关于二进制的利用
查看>>
进程关系
查看>>
非阻塞套接字实现并发处理
查看>>
[leetcode] 一些会的
查看>>
程序员的能力模型与沟通技巧
查看>>
天热了,的确,
查看>>
Unity3D 画笔实现系列02-UnityGL
查看>>
定时执行任务
查看>>
python多用户认证
查看>>
桥牌感悟 3
查看>>
CountHunter 6101 最优贸易 强联通缩点
查看>>
阿里云部署Java web项目初体验(转)
查看>>
Java NIO系列教程(三) Buffer(转)
查看>>
java转换字符串的编码(转)
查看>>
数据结构中的各种树
查看>>