信息产业部icp备案中心网站,新乡商城网站建设哪家好,企业画册设计制作公司,大连seo快速排名1.ArrayList简介 ArrayList的底层是数组队列#xff0c;相当于动态数组。与Java中的数组相比#xff0c;它的容量能动态增长。在添加大量元素前#xff0c;应用程序可以使用ensureCapacity操作来增加ArrayList实例的容量。这可以减少递增式再分配的数量。 ArrayList继承于Ab…1.ArrayList简介 ArrayList的底层是数组队列相当于动态数组。与Java中的数组相比它的容量能动态增长。在添加大量元素前应用程序可以使用ensureCapacity操作来增加ArrayList实例的容量。这可以减少递增式再分配的数量。 ArrayList继承于AbstructList实现了ListRandomAccessCloneableJava.io.Serializable这些接口。
public class ArrayListE extends AbstructListE implements ListE,RandomAccess,Cloneable,java.io.Serializable{} RandomAccess是一个标志接口表明实现这个接口的List集合是支持快速随机访问的。在ArrayList中我们即可以通过元素的序号快速获取元素对象这就是快速随机访问。ArrayList实现了Cloneable接口即覆盖了函数Clone能被克隆。ArrayList实现了java.io.Serializable接口这意味着ArrayList支持序列化能通过序列化去传输。 1.1ArrayList和Vector的区别
1.ArrayList是List的主要实现类底层使用Object[ ]存储适用于频繁地查找工作线程不安全。
2.Vector是List的古老实现类底层使用Object[ ]存储线程安全的。 1.2ArrayList与LinkedList区别
1.是否保证线程安全ArrayList和LinkedList都是不同步的也就是不保证线程安全
2.底层数据结构ArrayList底层使用的是Object数组LinkedList底层使用的是双向链表数据结构
3.插入和删除是否受元素位置的影响
ArrayList采用数组存储所以插入和删除元素的时间复杂度受元素位置影响。比如执行addE e方法的时候ArrayList会默认在将指定的元素追加到此列表的末尾这种情况时间复杂度就是O1。但是如果要在指定位置插入或者删除元素的话addint indexE element时间复杂度就为On-i。因为在进行上述操作的时候集合中第i和第i个元素之后的n-i个元素都要执行向后或者向前移一位的操作。
LinkedList采用链表存储 所以对于addE e方法的插入删除元素时间复杂度不受元素位置的影响近似O1如果要是在指定位置i插入和删除元素的话add (int index,E element)时间复杂度近似为On因为需要先移动到指定位置再插入。
4.是否支持快速随机访问LinkedList不支持高效的随机元素访问而ArrayList支持。快速随机访问就是通过元素的序号快速获取元素对象对应于getint index方法。
5.内存空间占用ArrayList的空间浪费主要体现在list列表的结尾会预留一定的容量空间而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间因为要存放直接后继和直接前驱以及数据。 2.ArrayList核心源码解读
package java.util;import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;public class ArrayListE extends AbstractListEimplements ListE, RandomAccess, Cloneable, java.io.Serializable
{private static final long serialVersionUID 8683452581122892189L;/*** 默认初始容量大小*/private static final int DEFAULT_CAPACITY 10;/*** 空数组用于空实例。*/private static final Object[] EMPTY_ELEMENTDATA {};//用于默认大小空实例的共享空数组实例。//我们把它从EMPTY_ELEMENTDATA数组中区分出来以知道在添加第一个元素时容量需要增加多少。private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA {};/*** 保存ArrayList数据的数组*/transient Object[] elementData; // non-private to simplify nested class access/*** ArrayList 所包含的元素个数*/private int size;/*** 带初始容量参数的构造函数用户可以在创建ArrayList对象时自己指定集合的初始大小*/public ArrayList(int initialCapacity) {if (initialCapacity 0) {//如果传入的参数大于0创建initialCapacity大小的数组this.elementData new Object[initialCapacity];} else if (initialCapacity 0) {//如果传入的参数等于0创建空数组this.elementData EMPTY_ELEMENTDATA;} else {//其他情况抛出异常throw new IllegalArgumentException(Illegal Capacity: initialCapacity);}}/***默认无参构造函数*DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为0.初始化为10也就是说初始其实是空数组 当添加第一个元素的时候数组容量才变成10*/public ArrayList() {this.elementData DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}/*** 构造一个包含指定集合的元素的列表按照它们由集合的迭代器返回的顺序。*/public ArrayList(Collection? extends E c) {//将指定集合转换为数组elementData c.toArray();//如果elementData数组的长度不为0if ((size elementData.length) ! 0) {// 如果elementData不是Object类型数据c.toArray可能返回的不是Object类型的数组所以加上下面的语句用于判断if (elementData.getClass() ! Object[].class)//将原来不是Object类型的elementData数组的内容赋值给新的Object类型的elementData数组elementData Arrays.copyOf(elementData, size, Object[].class);} else {// 其他情况用空数组代替this.elementData EMPTY_ELEMENTDATA;}}/*** 修改这个ArrayList实例的容量是列表的当前大小。 应用程序可以使用此操作来最小化ArrayList实例的存储。*/public void trimToSize() {modCount;if (size elementData.length) {elementData (size 0)? EMPTY_ELEMENTDATA: Arrays.copyOf(elementData, size);}}
//下面是ArrayList的扩容机制
//ArrayList的扩容机制提高了性能如果每次只扩充一个
//那么频繁的插入会导致频繁的拷贝降低性能而ArrayList的扩容机制避免了这种情况。/*** 如有必要增加此ArrayList实例的容量以确保它至少能容纳元素的数量* param minCapacity 所需的最小容量*/public void ensureCapacity(int minCapacity) {//如果是trueminExpand的值为0如果是false,minExpand的值为10int minExpand (elementData ! DEFAULTCAPACITY_EMPTY_ELEMENTDATA)// any size if not default element table? 0// larger than default for default empty table. Its already// supposed to be at default size.: DEFAULT_CAPACITY;//如果最小容量大于已有的最大容量if (minCapacity minExpand) {ensureExplicitCapacity(minCapacity);}}//得到最小扩容量private void ensureCapacityInternal(int minCapacity) {if (elementData DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 获取“默认的容量”和“传入参数”两者之间的最大值minCapacity Math.max(DEFAULT_CAPACITY, minCapacity);}ensureExplicitCapacity(minCapacity);}//判断是否需要扩容private void ensureExplicitCapacity(int minCapacity) {modCount;// overflow-conscious codeif (minCapacity - elementData.length 0)//调用grow方法进行扩容调用此方法代表已经开始扩容了grow(minCapacity);}/*** 要分配的最大数组大小*/private static final int MAX_ARRAY_SIZE Integer.MAX_VALUE - 8;/*** ArrayList扩容的核心方法。*/private void grow(int minCapacity) {// oldCapacity为旧容量newCapacity为新容量int oldCapacity elementData.length;//将oldCapacity 右移一位其效果相当于oldCapacity /2//我们知道位运算的速度远远快于整除运算整句运算式的结果就是将新容量更新为旧容量的1.5倍int newCapacity oldCapacity (oldCapacity 1);//然后检查新容量是否大于最小需要容量若还是小于最小需要容量那么就把最小需要容量当作数组的新容量if (newCapacity - minCapacity 0)newCapacity minCapacity;//再检查新容量是否超出了ArrayList所定义的最大容量//若超出了则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE//如果minCapacity大于MAX_ARRAY_SIZE则新容量则为Interger.MAX_VALUE否则新容量大小则为 MAX_ARRAY_SIZE。if (newCapacity - MAX_ARRAY_SIZE 0)newCapacity hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData Arrays.copyOf(elementData, newCapacity);}//比较minCapacity和 MAX_ARRAY_SIZEprivate static int hugeCapacity(int minCapacity) {if (minCapacity 0) // overflowthrow new OutOfMemoryError();return (minCapacity MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}/***返回此列表中的元素数。*/public int size() {return size;}/*** 如果此列表不包含元素则返回 true 。*/public boolean isEmpty() {//注意和的区别return size 0;}/*** 如果此列表包含指定的元素则返回true 。*/public boolean contains(Object o) {//indexOf()方法返回此列表中指定元素的首次出现的索引如果此列表不包含此元素则为-1return indexOf(o) 0;}/***返回此列表中指定元素的首次出现的索引如果此列表不包含此元素则为-1*/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)//equals()方法比较if (o.equals(elementData[i]))return i;}return -1;}/*** 返回此列表中指定元素的最后一次出现的索引如果此列表不包含元素则返回-1。.*/public int lastIndexOf(Object o) {if (o null) {for (int i size-1; i 0; i--)if (elementData[i]null)return i;} else {for (int i size-1; i 0; i--)if (o.equals(elementData[i]))return i;}return -1;}/*** 返回此ArrayList实例的浅拷贝。 元素本身不被复制。*/public Object clone() {try {ArrayList? v (ArrayList?) super.clone();//Arrays.copyOf功能是实现数组的复制返回复制后的数组。参数是被复制的数组和复制的长度v.elementData Arrays.copyOf(elementData, size);v.modCount 0;return v;} catch (CloneNotSupportedException e) {// 这不应该发生因为我们是可以克隆的throw new InternalError(e);}}/***以正确的顺序从第一个到最后一个元素返回一个包含此列表中所有元素的数组。*返回的数组将是“安全的”因为该列表不保留对它的引用。 换句话说这个方法必须分配一个新的数组。*因此调用者可以自由地修改返回的数组。 此方法充当基于阵列和基于集合的API之间的桥梁。*/public Object[] toArray() {return Arrays.copyOf(elementData, size);}/*** 以正确的顺序返回一个包含此列表中所有元素的数组从第一个到最后一个元素;*返回的数组的运行时类型是指定数组的运行时类型。 如果列表适合指定的数组则返回其中。*否则将为指定数组的运行时类型和此列表的大小分配一个新数组。*如果列表适用于指定的数组其余空间即数组的列表数量多于此元素则紧跟在集合结束后的数组中的元素设置为null 。*这仅在调用者知道列表不包含任何空元素的情况下才能确定列表的长度。*/SuppressWarnings(unchecked)public T T[] toArray(T[] a) {if (a.length size)// 新建一个运行时类型的数组但是ArrayList数组的内容return (T[]) Arrays.copyOf(elementData, size, a.getClass());//调用System提供的arraycopy()方法实现数组之间的复制System.arraycopy(elementData, 0, a, 0, size);if (a.length size)a[size] null;return a;}// Positional Access OperationsSuppressWarnings(unchecked)E elementData(int index) {return (E) elementData[index];}/*** 返回此列表中指定位置的元素。*/public E get(int index) {rangeCheck(index);return elementData(index);}/*** 用指定的元素替换此列表中指定位置的元素。*/public E set(int index, E element) {//对index进行界限检查rangeCheck(index);E oldValue elementData(index);elementData[index] element;//返回原来在这个位置的元素return oldValue;}/*** 将指定的元素追加到此列表的末尾。*/public boolean add(E e) {ensureCapacityInternal(size 1); // Increments modCount!!//这里看到ArrayList添加元素的实质就相当于为数组赋值elementData[size] e;return true;}/*** 在此列表中的指定位置插入指定的元素。*先调用 rangeCheckForAdd 对index进行界限检查然后调用 ensureCapacityInternal 方法保证capacity足够大*再将从index开始之后的所有成员后移一个位置将element插入index位置最后size加1。*/public void add(int index, E element) {rangeCheckForAdd(index);ensureCapacityInternal(size 1); // Increments modCount!!//arraycopy()这个实现数组之间复制的方法一定要看一下下面就用到了arraycopy()方法实现数组自己复制自己System.arraycopy(elementData, index, elementData, index 1,size - index);elementData[index] element;size;}/*** 删除该列表中指定位置的元素。 将任何后续元素移动到左侧从其索引中减去一个元素。*/public E remove(int index) {rangeCheck(index);modCount;E oldValue elementData(index);int numMoved size - index - 1;if (numMoved 0)System.arraycopy(elementData, index1, elementData, index,numMoved);elementData[--size] null; // clear to let GC do its work//从列表中删除的元素return oldValue;}/*** 从列表中删除指定元素的第一个出现如果存在。 如果列表不包含该元素则它不会更改。*返回true如果此列表包含指定的元素*/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;}/** Private remove method that skips bounds checking and does not* return the value removed.*/private void fastRemove(int index) {modCount;int numMoved size - index - 1;if (numMoved 0)System.arraycopy(elementData, index1, elementData, index,numMoved);elementData[--size] null; // clear to let GC do its work}/*** 从列表中删除所有元素。*/public void clear() {modCount;// 把数组中所有的元素的值设为nullfor (int i 0; i size; i)elementData[i] null;size 0;}/*** 按指定集合的Iterator返回的顺序将指定集合中的所有元素追加到此列表的末尾。*/public boolean addAll(Collection? extends E c) {Object[] a c.toArray();int numNew a.length;ensureCapacityInternal(size numNew); // Increments modCountSystem.arraycopy(a, 0, elementData, size, numNew);size numNew;return numNew ! 0;}/*** 将指定集合中的所有元素插入到此列表中从指定的位置开始。*/public boolean addAll(int index, Collection? extends E c) {rangeCheckForAdd(index);Object[] a c.toArray();int numNew a.length;ensureCapacityInternal(size numNew); // Increments modCountint numMoved size - index;if (numMoved 0)System.arraycopy(elementData, index, elementData, index numNew,numMoved);System.arraycopy(a, 0, elementData, index, numNew);size numNew;return numNew ! 0;}/*** 从此列表中删除所有索引为fromIndex 含和toIndex之间的元素。*将任何后续元素移动到左侧减少其索引。*/protected void removeRange(int fromIndex, int toIndex) {modCount;int numMoved size - toIndex;System.arraycopy(elementData, toIndex, elementData, fromIndex,numMoved);// clear to let GC do its workint newSize size - (toIndex-fromIndex);for (int i newSize; i size; i) {elementData[i] null;}size newSize;}/*** 检查给定的索引是否在范围内。*/private void rangeCheck(int index) {if (index size)throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}/*** add和addAll使用的rangeCheck的一个版本*/private void rangeCheckForAdd(int index) {if (index size || index 0)throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}/*** 返回IndexOutOfBoundsException细节信息*/private String outOfBoundsMsg(int index) {return Index: index, Size: size;}/*** 从此列表中删除指定集合中包含的所有元素。*/public boolean removeAll(Collection? c) {Objects.requireNonNull(c);//如果此列表被修改则返回truereturn batchRemove(c, false);}/*** 仅保留此列表中包含在指定集合中的元素。*换句话说从此列表中删除其中不包含在指定集合中的所有元素。*/public boolean retainAll(Collection? c) {Objects.requireNonNull(c);return batchRemove(c, true);}/*** 从列表中的指定位置开始返回列表中的元素按正确顺序的列表迭代器。*指定的索引表示初始调用将返回的第一个元素为next 。 初始调用previous将返回指定索引减1的元素。*返回的列表迭代器是fail-fast 。*/public ListIteratorE listIterator(int index) {if (index 0 || index size)throw new IndexOutOfBoundsException(Index: index);return new ListItr(index);}/***返回列表中的列表迭代器按适当的顺序。*返回的列表迭代器是fail-fast 。*/public ListIteratorE listIterator() {return new ListItr(0);}/***以正确的顺序返回该列表中的元素的迭代器。*返回的迭代器是fail-fast 。*/public IteratorE iterator() {return new Itr();} 3.ArrayList扩容机制分析 3.1先从ArrayList的构造函数说起
JDK8ArrayList有三种方式来初始化构造方法源码如下 /*** 默认初始容量大小*/private static final int DEFAULT_CAPACITY 10;private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA {};/***默认构造函数使用初始容量10构造一个空列表(无参数构造)*/public ArrayList() {this.elementData DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}/*** 带初始容量参数的构造函数。用户自己指定容量*/public ArrayList(int initialCapacity) {if (initialCapacity 0) {//初始容量大于0//创建initialCapacity大小的数组this.elementData new Object[initialCapacity];} else if (initialCapacity 0) {//初始容量等于0//创建空数组this.elementData EMPTY_ELEMENTDATA;} else {//初始容量小于0抛出异常throw new IllegalArgumentException(Illegal Capacity: initialCapacity);}}/***构造包含指定collection元素的列表这些元素利用该集合的迭代器按顺序返回*如果指定的集合为nullthrows NullPointerException。*/public ArrayList(Collection? extends E c) {elementData c.toArray();if ((size elementData.length) ! 0) {// c.toArray might (incorrectly) not return Object[] (see 6260652)if (elementData.getClass() ! Object[].class)elementData Arrays.copyOf(elementData, size, Object[].class);} else {// replace with empty array.this.elementData EMPTY_ELEMENTDATA;}}
以无参构造方法创建ArrayList时实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时数组容量扩为10。
3.2 一步一步分析ArrayList扩容机制
这里以无参构造函数创建的ArrayList为例分析
3.2.1先来看add方法 /*** 将指定的元素追加到此列表的末尾。*/public boolean add(E e) {//添加元素之前先调用ensureCapacityInternal方法ensureCapacityInternal(size 1); // Increments modCount!!//这里看到ArrayList添加元素的实质就相当于为数组赋值elementData[size] e;return true;}
3.2.2再来看ensureCapacityInternal方法
JDK7可以看到add方法首先调用了ensureCapacityInternalsize1 //得到最小扩容量private void ensureCapacityInternal(int minCapacity) {if (elementData DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 获取默认的容量和传入参数的较大值minCapacity Math.max(DEFAULT_CAPACITY, minCapacity);}ensureExplicitCapacity(minCapacity);} 当add进第一个元素时mainCapacity为1在Math.max()方法比较后minCapacity为10
3.2.3 ensureExplicitcapacity()方法
如果调用 ensureCapacityInternal()方法就一定会进入执行这个方法 //判断是否需要扩容private void ensureExplicitCapacity(int minCapacity) {modCount;// overflow-conscious codeif (minCapacity - elementData.length 0)//调用grow方法进行扩容调用此方法代表已经开始扩容了grow(minCapacity);}
当我们要 add 进第 1 个元素到 ArrayList 时elementData.length 为 0 因为还是一个空的 list因为执行了 ensureCapacityInternal() 方法 所以 minCapacity 此时为 10。此时minCapacity - elementData.length 0成立所以会进入 grow(minCapacity) 方法。当 add 第 2 个元素时minCapacity 为 2此时 e lementData.length(容量)在添加第一个元素后扩容成 10 了。此时minCapacity - elementData.length 0 不成立所以不会进入 执行grow(minCapacity) 方法。添加第 3、4···到第 10 个元素时依然不会执行 grow 方法数组容量都为 10。
直到添加第 11 个元素minCapacity(为 11)比 elementData.length为 10要大。进入 grow 方法进行扩容。 3.2.4 grow方法 /*** 要分配的最大数组大小*/private static final int MAX_ARRAY_SIZE Integer.MAX_VALUE - 8;/*** ArrayList扩容的核心方法。*/private void grow(int minCapacity) {// oldCapacity为旧容量newCapacity为新容量int oldCapacity elementData.length;//将oldCapacity 右移一位其效果相当于oldCapacity /2//我们知道位运算的速度远远快于整除运算整句运算式的结果就是将新容量更新为旧容量的1.5倍int newCapacity oldCapacity (oldCapacity 1);//然后检查新容量是否大于最小需要容量若还是小于最小需要容量那么就把最小需要容量当作数组的新容量if (newCapacity - minCapacity 0)newCapacity minCapacity;// 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) hugeCapacity() 方法来比较 minCapacity 和 MAX_ARRAY_SIZE//如果minCapacity大于最大容量则新容量则为Integer.MAX_VALUE否则新容量大小则为 MAX_ARRAY_SIZE 即为 Integer.MAX_VALUE - 8。if (newCapacity - MAX_ARRAY_SIZE 0)newCapacity hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData Arrays.copyOf(elementData, newCapacity);}
int newCapacity oldCapacity (oldCapacity 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右oldCapacity 为偶数就是 1.5 倍否则是 1.5 倍左右 奇偶不同比如 1010/2 15, 3333/249。如果是奇数的话会丢掉小数。 移位运算符1 右移一位相当于除 2右移 n 位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了 1 位所以相当于 oldCapacity /2。对于大数据的 2 进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源 当 add 第 1 个元素时oldCapacity 为 0经比较后第一个 if 判断成立newCapacity minCapacity(为 10)。但是第二个 if 判断不会成立即 newCapacity 不比 MAX_ARRAY_SIZE 大则不会进入 hugeCapacity 方法。数组容量为 10add 方法中 return true,size 增为 1。当 add 第 11 个元素进入 grow 方法时newCapacity 为 15比 minCapacity为 11大第一个 if 判断不成立。新容量没有大于数组最大 size不会进入 hugeCapacity 方法。数组容量扩为 15add 方法中 return true,size 增为 11。以此类推······ 注意 java 中的 length属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性.java 中的 length() 方法是针对字符串说的,如果想看这个字符串的长度则用到 length() 这个方法.java 中的 size() 方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看 3.2.5 hugeCapaty()方法 从上面 grow() 方法源码我们知道 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) hugeCapacity() 方法来比较 minCapacity 和 MAX_ARRAY_SIZE如果 minCapacity 大于最大容量则新容量则为Integer.MAX_VALUE否则新容量大小则为 MAX_ARRAY_SIZE 即为 Integer.MAX_VALUE - 8。 private static int hugeCapacity(int minCapacity) {if (minCapacity 0) // overflowthrow new OutOfMemoryError();//对minCapacity和MAX_ARRAY_SIZE进行比较//若minCapacity大将Integer.MAX_VALUE作为新数组的大小//若MAX_ARRAY_SIZE大将MAX_ARRAY_SIZE作为新数组的大小//MAX_ARRAY_SIZE Integer.MAX_VALUE - 8;return (minCapacity MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;} 3.3 System.arraycopy() 和Arrays.copyOf()方法
阅读源码的话我们就会发现 ArrayList 中大量调用了这两个方法。比如我们上面讲的扩容操作以及add(int index, E element)、toArray() 等方法中都用到了该方法
3.3.1 System.arraycopy()方法 /*** 在此列表中的指定位置插入指定的元素。*先调用 rangeCheckForAdd 对index进行界限检查然后调用 ensureCapacityInternal 方法保证capacity足够大*再将从index开始之后的所有成员后移一个位置将element插入index位置最后size加1。*/public void add(int index, E element) {rangeCheckForAdd(index);ensureCapacityInternal(size 1); // Increments modCount!!//arraycopy()方法实现数组自己复制自己//elementData:源数组;index:源数组中的起始位置;elementData目标数组index 1目标数组中的起始位置 size - index要复制的数组元素的数量System.arraycopy(elementData, index, elementData, index 1, size - index);elementData[index] element;size;}
一个简单的方法测试
public class ArraycopyTest {public static void main(String[] args) {// TODO Auto-generated method stubint[] a new int[10];a[0] 0;a[1] 1;a[2] 2;a[3] 3;System.arraycopy(a, 2, a, 3, 3);a[2]99;for (int i 0; i a.length; i) {System.out.print(a[i] );}}}
结果
0 1 99 2 3 0 0 0 0 0
3.3.2 Arrays.copyOf()方法 /**以正确的顺序返回一个包含此列表中所有元素的数组从第一个到最后一个元素; 返回的数组的运行时类型是指定数组的运行时类型。*/public Object[] toArray() {//elementData要复制的数组size要复制的长度return Arrays.copyOf(elementData, size);}
个人觉得使用 Arrays.copyOf()方法主要是为了给原有数组扩容测试代码如下
public class ArrayscopyOfTest {public static void main(String[] args) {int[] a new int[3];a[0] 0;a[1] 1;a[2] 2;int[] b Arrays.copyOf(a, 10);System.out.println(b.lengthb.length);}
}
结果
10 3.3 两者联系和区别
联系
看两者源代码可以发现 copyOf()内部实际调用了 System.arraycopy() 方法
区别
arraycopy() 需要目标数组将原数组拷贝到你自己定义的数组里或者原数组而且可以选择拷贝的起点和长度以及放入新数组中的位置 copyOf() 是系统自动在内部新建一个数组并返回该数组。 3.4 ensureCapacity 方法
ArrayList 源码中有一个 ensureCapacity 方法不知道大家注意到没有这个方法 ArrayList 内部没有被调用过所以很显然是提供给用户调用的那么这个方法有什么作用呢 /**如有必要增加此 ArrayList 实例的容量以确保它至少可以容纳由minimum capacity参数指定的元素数。** param minCapacity 所需的最小容量*/public void ensureCapacity(int minCapacity) {int minExpand (elementData ! DEFAULTCAPACITY_EMPTY_ELEMENTDATA)// any size if not default element table? 0// larger than default for default empty table. Its already// supposed to be at default size.: DEFAULT_CAPACITY;if (minCapacity minExpand) {ensureExplicitCapacity(minCapacity);}} 最好在 add 大量元素之前用 ensureCapacity 方法以减少增量重新分配的次数 我们通过下面的代码实际测试以下这个方法的效果
public class EnsureCapacityTest {public static void main(String[] args) {ArrayListObject list new ArrayListObject();final int N 10000000;long startTime System.currentTimeMillis();for (int i 0; i N; i) {list.add(i);}long endTime System.currentTimeMillis();System.out.println(使用ensureCapacity方法前(endTime - startTime));}
}
运行结果
使用ensureCapacity方法前2158
public class EnsureCapacityTest {public static void main(String[] args) {ArrayListObject list new ArrayListObject();final int N 10000000;list new ArrayListObject();long startTime1 System.currentTimeMillis();list.ensureCapacity(N);for (int i 0; i N; i) {list.add(i);}long endTime1 System.currentTimeMillis();System.out.println(使用ensureCapacity方法后(endTime1 - startTime1));}
}
运行结果
使用ensureCapacity方法前1773
通过运行结果我们可以看出向 ArrayList 添加大量元素之前最好先使用ensureCapacity 方法以减少增量重新分配的次数。