Java集合源码--ArrayList
来源:互联网 发布:js 将时分秒转换成数字 编辑:程序博客网 时间:2024/06/08 02:26
最近学习了一下常用的两种动态代理技术(JDK动态代理和CGLIB动态代理),感觉Java是真的妙不可言。越学越有趣,原来我们虽然在实际项目中会去配置<aop:config>,可是不清楚到底为什么XML中写几个配置就可以完成日志打印,事务管理,权限控制的功能了,知道了动态代理的技术结合Java的反射就不难了解了,AOP说白了就是复杂版的HellWorld动态代理。关于CGLIB(Code Generation Libirary)动态代理的例子后面另写一篇文章吧。电脑上没有cglib的jar包,不好测试,cglib的好处是不需要接口,提供一个实现类即可完成动态代理的技术,重点是实现一个对父类方法调用的拦截器。把实际要加的代码加在involeSuper前后,如果类是Final的不能被继承的话就不能使用Cglib代理,当然这是后话。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
学习Java一定要看源码,这是一个艰苦的过程,源码一般比较枯燥,不管是集合这部分还是难懂的Spring,但是这也是一个很好的思考的过程,这也是为什么新目录叫“血战源码“了,ok,今天我们先学习下最常用也是最基础的集合ArrayList。
1.定义
我们程序中经常会用到ArrayList,相信大家对这句代码不会陌生。
List<String> list = new ArrayList<String>();
我们定义一个String类型的ArrayList,Ctrl+左键点进去,点到ArrzyList里面去,发现ArrayList定义的代码如下:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ private static final long serialVersionUID = 8683452581122892189L; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. */ private transient Object[] elementData; /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size;
ArrayList继承AbstractList,实现了RandomAccess(随机读取),Cloneable(可克隆),Serializable(可序列化)。
存储数据元素的其实是一个底层数组elementData,这个数组前面的关键字transient表明了这个数组不支持序列化(因为大部分情况下不需要完全序列化这个数组,不一定一直是慢的,如果数组很长,元素很少,大部分为空的话就没有序列化的必要了)。
size是ArrayList的大小。
当我们new ArrayList的时候,系统默认分配的长度为10。
public ArrayList() {this(10); }2.新增(list.add())
现在我们写一个添加操作,然后debug调试跟踪一下,
public class Test {public static void main(String[] args) {List<String> list = new ArrayList<String>();list.add("hello");进入add方法的源码看一下,
public boolean add(E e) {ensureCapacity(size + 1); // Increments modCount!!elementData[size++] = e;return true; }
分两步操作。
(1)第一步后面注释就是增加modCount变量,这个modCount是执行操作的次数;
(2)第二步是很常见的赋值操作。
第一步方法的代码如下:
/** * Increases the capacity of this <tt>ArrayList</tt> instance, if * necessary, to ensure that it can hold at least the number of elements * specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ public void ensureCapacity(int minCapacity) {modCount++;int oldCapacity = elementData.length;if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3)/2 + 1; if (newCapacity < minCapacity)newCapacity = minCapacity; // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity);} }这个方法的意思是如果有必要扩充ArrayList实例的空间,确保它至少能装下minimum capacity个数的元素。如上面所说,初始化的时候分配的长度为10,默认为null,现在我们要添加元素,大致过程是这样的。
(1)设置10个长度的空间,list.add("hello");之后,会发现第一个部分被Hello对象填充,其他9个还是null;modcount变为1;
(2)接下来在执行9次
for(int i = 0;i<9;i++){list.add("sgx");}发现当前List的内容是[hello, sgx, sgx, sgx, sgx, sgx, sgx, sgx, sgx, sgx],此时我们再次添加一个内容,调试跟踪:
此时modCount=11,old capacity=10,min capacity=11,观察上面的第一部分的代码,会扩充空间,新的elementData的空间大小为new capacity = (10*3)/2+1=16,
这个方法是别人给定的,为什么刚好是这个方法?应该是这个数值算一个合理数据,不至于过多次扩充空间。
Arrays.copyOf方法:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }
创建了一个新的elementData【16】,然后将原来的elementData复制过去,现在的elementData是这样的:
[hello, sgx, sgx, sgx, sgx, sgx, sgx, sgx, sgx, sgx, null, null, null, null, null, null]
list的内容没有后面的null而已。
3.删除(list.remove())
现在我们又在刚才的基础上删除掉一个元素,看看源码是如何执行的;
public boolean remove(Object o) {if (o == null) { for (int index = 0; index < size; index++)if (elementData[index] == null) { fastRemove(index); return true;}} else { for (int index = 0; index < size; index++)if (o.equals(elementData[index])) { fastRemove(index); return true;} }return false; }代码很明显,会先判断当前的对象是否是空,不为空的话,执行FastRemove()方法;
private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work }
fastRemove就是把要删除的元素开始的后面的部分整体前移,然后最后一个置为null即可。gc会自动回收没有用的这个元素。
4.总结
优点:其实ArrayList很容易让我想起刚学数组的时候,当时的插入、删除读取跟这个是一样的,因为ArrayList底层存储数据的就是数组;那么它的优点也就是数组的优点,
我们知道:数组是随机读取的,获取某个元素非常快;
缺点: 但是插入和删除由于涉及到扩充空间,如果扩充空间次数多的时候需要不停复制一段elementData数组,耗费性能。而且ArrayList明显不是线程安全的,很明显,没有同步修饰符;
- Java集合源码--ArrayList
- java集合-ArrayList源码剖析
- Java集合源码解析-ArrayList
- Java集合ArrayList源码解析
- Java集合-ArrayList源码分析
- 【java集合】ArrayList源码分析
- 【Java集合源码剖析】ArrayList源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- C++写插入排序
- GameEntity(十一)—— Iplayer
- HDU-1798 Tell me the area(数学)
- 软件工程课程实验报告:实验七
- 线性时间求第k小(分治思想)
- Java集合源码--ArrayList
- Tensorflow 从bin文件中读取数据并
- STM32寄存器编程思路
- spark机器学习MLlib笔记(1)
- Android JobScheduler
- JS-变量,作用域和内存问题
- 程序猿的学习之路 新起点
- Android --- Extras(自定义类型传递对类型做序列化)
- 高精度模板