java集合之ArrayList解析
来源:互联网 发布:车载蓝牙软件下载 编辑:程序博客网 时间:2024/06/06 04:09
ArrayList是基于动态数组实现增删改查操作,进行顺序存储的,在内存中各个元素的地
址是连续的.
ArrayList组织结构图:
根据上面的结构图知道ArrayList继承AbstractList并实现了RandomAccess,Cloneable,Serializable这三个标记接口,所以AbstractList具有如下特性:
- AbstractList:实现了增删改查的集合操作,并可以对集合进行迭代查询。
- RandomAccess:随机访问性能很高.
- Cloneable:ArrayList对象可以进行克隆.
- Serializable:ArrayList对象可以进行序列化和反序列化存储和读取.
ArrayList简要基本用法:
public boolean add(E var1)//在集合末尾添加数据var1
public void add(int index, E var2)//在索引位置为index的位置处插入数据var2
public E remove(int index)//删除集合中索引位置为index的元素.
public boolean remove(Object var1)//删除集合中值为var1的元素,查找元素var1是用var1的equals方法比对的,如果var1是null,就查找集合中值为null的元素并删除.(这里需要注意的是删除的元素是集合中第一个为var1或null的元素,并不是所有).
public E set(int index, E var2)//修改索引为index元素的值为var2;
public E get(int index)//获取索引index处的元素数据.
ArrayList的遍历:
增强式for循环
ArrayList<Integer> arrayList=new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); for (Integer integer:arrayList){ System.out.println(integer); }
Iterator迭代
ArrayList<Integer> arrayList=new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); Iterator<Integer> iterator=arrayList.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); }
ArrayList的元素过滤
Iterator迭代过滤
ArrayList<Integer> arrayList=new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); Iterator<Integer> iterator=arrayList.iterator(); while (iterator.hasNext()){ Integer integer=iterator.next(); if(integer>=3){//删除ArrayList集合中大于等于3的所有元素 iterator.remove(); } }
for循环过滤
抛出java.util.ConcurrentModificationException异常,所以这种方式是错误的,后面会介绍原因.
ArrayList<Integer> arrayList=new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); for(Integer e:arrayList){ if(e>=3){ arrayList.remove(e); } }
ArrayList源码解析
成员变量说明:
private static final int DEFAULT_CAPACITY = 10默认存储空间为10
private static final Object[] EMPTY_ELEMENTDATA = new Object[0]空动态数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];空容量数组
transient Object[] elementData;动态获取存储地址的数组elementData,它是不可以进行序列化的,作用与static修饰符一样.
private int size;数组中有效元素的个数。
private static final int MAX_ARRAY_SIZE = 2147483639;//动态数组的最大容量.
构造函数说明:
默认构造函数,创建一个空容量的数组给elementData
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
创建指定容量的数组集合。
public ArrayList(int var1) { if(var1 > 0) {//大于0创建指定长度的数组给elementData this.elementData = new Object[var1]; } else { if(var1 != 0) { throw new IllegalArgumentException("Illegal Capacity: " + var1);//如果小于0抛出异常 } this.elementData = EMPTY_ELEMENTDATA;//若等于0赋值空数组. } }
创建一个初始数据为var1的集合给elementData;
public ArrayList(Collection<? extends E> var1) { this.elementData = var1.toArray();//首先调用集合的toArray转换为数组并赋值给elementData if((this.size = this.elementData.length) != 0) { if(this.elementData.getClass() != Object[].class) { this.elementData = Arrays.copyOf(this.elementData, this.size, Object[].class); } } else { this.elementData = EMPTY_ELEMENTDATA;//长度为0,赋值空数组 } }
公共方法解析:
add方法源码:
public boolean add(E var1) { this.ensureCapacityInternal(this.size + 1); this.elementData[this.size++] = var1; return true; }
1.它首先调用ensureCapacityInternal确保数组元素的空间足够, ensureCapacityInternal源码如下:
private void ensureCapacityInternal(int var1) { if(this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { var1 = Math.max(10, var1); } this.ensureExplicitCapacity(var1); }
2.判断数组是不是空的,如果是空的,则首次为它分配的容量至少为10,然后调用ensureExplicitCapacity.
private void ensureExplicitCapacity(int var1) { ++this.modCount; if(var1 - this.elementData.length > 0) { this.grow(var1); } }
3.modCount表示内部修改次数(后面会解释),判断需要的长度是否大于当前数组elementData的长度,如果是就执行grow方法进行扩容.
private void grow(int var1) { int var2 = this.elementData.length; int var3 = var2 + (var2 >> 1);//右移一位相当于除2,所以是1.5倍 if(var3 - var1 < 0) { var3 = var1; } if(var3 - 2147483639 > 0) { var3 = hugeCapacity(var1); } this.elementData = Arrays.copyOf(this.elementData, var3); }
4.首先将当前数组的长度扩容为1.5倍,如果此时扩容后的容量仍然小于需要的长度,就将当前数组的长度改为需要的长度.
public void add(int index, E var2) { this.rangeCheckForAdd(var1); this.ensureCapacityInternal(this.size + 1); System.arraycopy(this.elementData, var1, this.elementData, var1 + 1, this.size - var1); this.elementData[var1] = var2; ++this.size; }
这个方法与add(E var1)差不多,实现的思想是首先判断索引是否越界,如果越界抛出IndexOutOfBoundsException异常,否则执行确保容量足够的方法,其次将index索引后的所有元素向后移动, z在index索引处插入元素var1,最后修改size+1;
remove操作
public E remove(int var1) { this.rangeCheck(var1); ++this.modCount; Object var2 = this.elementData(var1); int var3 = this.size - var1 - 1; if(var3 > 0) { System.arraycopy(this.elementData, var1 + 1, this.elementData, var1, var3); } this.elementData[--this.size] = null; return var2; }
首先检查是否数组越界,如果没有越界执行modCount+1,修改modCound的值,然后获得数组elemntData中索引为var1的元素,计算index数组复制的便宜量,即将index+1处的所有元素向前移动,最后修改数组末尾的元素值为null,等待GC回收并返回删除的元素.
public boolean remove(Object var1) { int var2; if(var1 == null) { for(var2 = 0; var2 < this.size; ++var2) { if(this.elementData[var2] == null) { this.fastRemove(var2); return true; } } } else { for(var2 = 0; var2 < this.size; ++var2) { if(var1.equals(this.elementData[var2])) { this.fastRemove(var2); return true; } } } return false; } private void fastRemove(int var1) { ++this.modCount; int var2 = this.size - var1 - 1; if(var2 > 0) { System.arraycopy(this.elementData, var1 + 1, this.elementData, var1, var2); } this.elementData[--this.size] = null; }
remove(Object var1)删除数组元素需要区分null和非null值,非null值比较的对象是用equals.上面源码比较简单不再重复赘述.
清空ArrayList集合元素
public void clear() { ++this.modCount; for(int var1 = 0; var1 < this.size; ++var1) { this.elementData[var1] = null; } this.size = 0; }
实现思想是:迭代数组中的元素分别设置为null,修改size为0,modCount++;
ArrayList实现了Iterable接口
public interface Iterable<T> { Iterator<T> iterator();}
子类需要实现iterator()方法并返回Iterator对象
public interface Iterator<E> { boolean hasNext(); E next(); default void remove() { throw new UnsupportedOperationException("remove"); }
调用ArrayList的iterator()方法调用下面的逻辑实现:
public Iterator<E> iterator() { return new ArrayList.Itr(); } private class Itr implements Iterator<E> { int cursor; int lastRet; int expectedModCount; private Itr() { this.lastRet = -1; this.expectedModCount = ArrayList.this.modCount; } public boolean hasNext() { return this.cursor != ArrayList.this.size; } public E next() { this.checkForComodification(); int var1 = this.cursor; if(var1 >= ArrayList.this.size) { throw new NoSuchElementException(); } else { Object[] var2 = ArrayList.this.elementData; if(var1 >= var2.length) { throw new ConcurrentModificationException(); } else { this.cursor = var1 + 1; return var2[this.lastRet = var1]; } } } public void remove() { if(this.lastRet < 0) { throw new IllegalStateException(); } else { this.checkForComodification(); try { ArrayList.this.remove(this.lastRet); this.cursor = this.lastRet; this.lastRet = -1; this.expectedModCount = ArrayList.this.modCount; } catch (IndexOutOfBoundsException var2) { throw new ConcurrentModificationException(); } } }
实际上得到的是内部类Itr的实例,注意上面执行next()方法每次迭代器操作的时候都要首先调用checkForComodification(),它是什么呢?
final void checkForComodification() { if(ArrayList.this.modCount != this.expectedModCount) { throw new ConcurrentModificationException(); } }
看到了吧它就是用来比对modCount与expectModCount的值,这里就知道了为什么对集合进行删除,添加的时候需要修改modCount的值,当二者不等的时候抛出异常.
iterator执行remove()方法时会执行this.expectedModCount = ArrayList.this.modCount;二者永远相等所以进行迭代执行过滤操作的时候不会出现异常。而for循环是发生了并发修改异常,为什么呢?迭代器内部会维护一些索引位置相关的数据,要求在迭代过程中,容器不能发生结构性变化,否则这些索引位置就失效了。所谓结构性变化就是添加、插入和删除元素,只是修改元素内容不算结构性变化。
ArrayList的特点:
- 查询效率高
- 删除和添加元素需要移动数组所以效率比较低
- 与LinkList正好相反,后面会分析LinkList的实现源码。
- 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源码解析
- Linux 常用实用命令整合
- BING算法——思路整理(目标检测算法)
- Android->build.gradle->dataBinding,dexOptions,lintOptions
- 简单实用的CRM系统-可灵活自定义设计的crm系统
- 启用Spring MVC
- java集合之ArrayList解析
- usbmouse的注释
- “System.StackOverflowException”类型的未经处理的异常在 MySql.Data.dll 中发生”的错误处理
- 导入约束
- 企业怎么看培训出来的人,这个问题太简单了
- log4j2入门教程(二)简介2(propreties配置)
- 解决虚拟机linux端mysql数据库无法远程访问
- 通用分页
- Paxos协议 学习小结