java arraylist无序吗_Java技能书1——ArrayList

0.常用接口

0.1 Queue接口

0.1.1 注意事项

队列接口,且不允许null元素,因为队列使用poll()方法是否返回null来判断队列是否为空,但是LinkedList是允许null元素的,因此,当LinkedList作为队列实现时,不应该将null元素插入队列。

0.1.2 常用方法

boolean add(E e);

将对象e插入队列尾部,成功返回true,失败(没有空间)抛出异常IllegalStateException;

boolean offer(E e);

将对象e插入队列尾部,成功返回true,失败(没有空间)返回false;

E remove();

获取并移除队列头部元素,如果队列为空,抛出NoSuchElementException异常;

E poll();

获取并移除队列头部元素,如果队列为空,返回null;

E element();

获取但不移除队列头部元素,如果队列为空,抛出NoSuchElementException异常;

E peek();

获取但不移除队列头部元素,如果队列为空,返回null;

基于LinkedList实现的队列

package com.pichen.basis.col;

import java.util.LinkedList;

import java.util.Queue;

public class LinkListTest {

public static void main(String[] args) {

Queue linkedListQueue = new LinkedList();

//入队

linkedListQueue.offer(3);

linkedListQueue.offer(4);

linkedListQueue.offer(2);

linkedListQueue.offer(1);

//出队

Integer tmp;

while((tmp = linkedListQueue.poll()) != null){

System.out.println(tmp);

}

System.out.println(linkedListQueue.peek());

}

}

0.2 Deque接口

双端队列接口,继承队列接口,支持在队列两端进行入队和出队操作;

除了Collection接口Queue接口中定义的方法外,Deque还包括以下方法

void addFirst(E e);

将对象e插入到双端队列头部,容间不足时,抛出IllegalStateException异常;

void addLast(E e);

将对象e插入到双端队列尾部,容间不足时,抛出IllegalStateException异常;

boolean offerFirst(E e);

将对象e插入到双端队列头部

boolean offerLast(E e);

将对象e插入到双端队列尾部;

E removeFirst();

获取并移除队列第一个元素,队列为空,抛出NoSuchElementException异常;

E removeLast();

获取并移除队列最后一个元素,队列为空,抛出NoSuchElementException异常;

E pollFirst();

获取并移除队列第一个元素,队列为空,返回null;

E pollLast();

获取并移除队列最后一个元素,队列为空,返回null;

E getFirst();

获取队列第一个元素,但不移除,队列为空,抛出NoSuchElementException异常;

E getLast();

获取队列最后一个元素,但不移除,队列为空,抛出NoSuchElementException异常;

E peekFirst();

获取队列第一个元素,队列为空,返回null;

E peekLast();

获取队列最后一个元素,队列为空,返回null;

boolean removeFirstOccurrence(Object o);

移除第一个满足 (o==null ? e==null : o.equals(e)) 的元素

boolean removeLastOccurrence(Object o);

移除最后一个满足 (o==null ? e==null : o.equals(e)) 的元素

void push(E e);

将对象e插入到双端队列头部;

E pop();

移除并返回双端队列的第一个元素

Iterator descendingIterator();

双端队列尾部到头部的一个迭代器;

0.3 AbstractSequentialList类

基于迭代器利用索引index实现数据的随机访问;

public E get(int index)

public E set(int index, E element)

public void add(int index, E element)

public E remove(int index)

public boolean addAll(int index, Collection extends E> c)

1.List

1.1 ArrayList

ArrayList为动态数组,有如下优点:- 动态的增加和减少元素;

- 实现了ICollection和List接口

- 灵活的设置数组的大小

1.1.1 Array

1.1.1.1 Arrays.toString():将int数组转字符串数组

格式:Arrays.toString(数组名)

将数组转化成字符串,此时输出的结果是字符串类型。

int arr[] = {1,2,3,4,5,6};

String newArr = Arrays.toString(arr);

输出:[1, 2, 3, 4, 5, 6]

1.1.1.2 Arrays.copyOf(数组名,扩容后长度) 数组扩容

1.1.1.2.1 格式

格式:Arrays.copyOf(数组名,扩容后长度)

复制全部元素,并指定新数组长度

int[] arr = {3, 7, 2, 1};

int[] newArr = Arrays.copyOf(arr, 10);//指定新数组的长度

复制从n到m(但是不包括m)的元素

//只复制从索引[1]到索引[3]之间的元素,但是不包含索引[3]的元素

int[] newArr2 = Arrays.copyOfRange(arr, 1, 3);

输出:1 2 3 4 0 0 0 0

注意:此方法可以用于扩容,也可以用于缩容,改变其第二个参数即可。

1.1.1.2.2 Arrays.copyOf源码分析

Arrays.copyOf(U[] original, int neLength, Class extends T[]> newType) 根据class类型决定是new还是反射去构造一个泛型数组,同时利用native函数,批量赋值元素至新数组中。

public static T[] copyOf(U[] original, int newLength, Class extends T[]> newT){

@SupperessWarnings("unchecked")

//根据class类型来决定是new还是反射构造一个泛型数组

T[] copy = ((Object)newType == (Object)Object[].class) ? new Object[newLength] :(T[])Array.newInstance(newType.getComponentType(), newLength);

//利用native函数,批量赋值元素至新数组中

System.arrayCopy(original, 0, copy, 0, Math.min(original.length, newLength));

return copy;

}

1.1.1.3 System.arraycopy()

1.1.1.3.1 格式

格式:System.arraycopy(原数组,原数组起始位置,新数组,新数组起始位置,复制长度)

int arr[] = {1,2,3,4};

int[] arr1 = new int[6];

System.arraycopy(arr, 0, arr1, 1, 3);

输出:0 1 2 3 0 0

1.1.1.3.2 源码

@FastNative

public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

1.1.1.4 Arrays.sort()排序

1.1.1.4.1 全数组排序

格式:Arrays.sort(数组名)

注意:只能做升序排序,不能做降序排序。

int arr[] = { 2, 5, -2, 6, -3, 8, 0, -7, -9, 4 };

Arrays.sort(arr);

1.1.1.4.2 部分数组排序

Arrays.sort(arr, 2, 5);

从索引[2]到索引[5]的元素进行排序,包括索引[5]

1.1.1.5 Arrays.fill()填充

格式:Arrays.fill(数组名 ,开始位置 , 结束位置, 填入的值)

int arr[] = {1,2,3,4,5,6,7,8,9,10};

Arrays.fill(arr, 3, 6, 50);

int array[] = new int[6];

Arrays.fill(array, 100);

结果为:

1 2 3 50 50 50 7 8 9 10

100 100 100 100 100 100

1.1.1.6 去重

使用set无序但是无重复项的特性

int[] arr11 = {1, 2, 3, 4,5,6,7,8,9,0,3,2,4,5,6,7,4,32,2,1,1,4,6,3};

Set set = new HashSet<>();

for(int i = 0; i < arr11.length; i++){

set.add(arr11[i]);

}

1.1.1.7 Arrays.equals(arr1, arr2)判定两个数组是否相同

boolean Arrays.equals(arr1, arr2);

1.1.1.8 数组转为list集合

String[] arr = {"1","2","3"};

List list = Arrays.asList(arr);

1.1.1.9 数组转为set

String[] arr = {"1","2","3"};

Set set = new HashSet(Arrays.asList(arr));

1.1.1.10 数组中是否包含一个值

Arrays.asList(arr).contains("1");

1.1.1.11 从array创建arrayList

ArrayList arrayList = new ArrayList(Arrays.asList(arr));

1.1.2 构造方法

public ArrayList(){

this(10);

//调用ArrayList(10) 默认初始化一个大小为10的object数组

}

public ArrayList(int initialCapacity){

if(initialCapacity < 0){

throw new IllegalArgumentException("IllegalCapacity:" + initialCapacity);

}

//如果用户初始化大小小于0抛异常,否则新建一个用户初始值大小的object数组

this.elementData = new Object[initialCapacity];

}

public ArrayList(Collection extends E> c){

elementData = c.toArray();

size = elementData.length;

// 当c.toArray返回的不是object类型的数组时,进行下面转化

if(elementData.getClass() != Object[].class){

elementData = Arrays.copyOf(elementData, size, Object[].class);

}

}

由上面三种构造方法可知,默认情况下使用ArrayList会生成一个大小为10的Object类型的数组。也可以调用ArrayList(int initialCapacity) 来初始化Object数组的大小。并且用户可以往ArrayList中传入一个容器只要这个容器是Collection类型的。调用ArrayList(Collection extends E> c)接口的时候会将容器数组化处理并将这个数组值赋给Object数组。

1.1.3 ArrayList的线程安全性

1.1.3.1 线程不安全

public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable{

/**

* 列表元素集合数组

* 如果新建ArrayList对象时没有指定大小,那么会将EMPTY_ELEMENTDATA赋值给elementData,

* 并在第一次添加元素时,将列表容量设置为DEFAULT_CAPACITY

*/

transient Object[] elementData;

//列表大小,elementData的存储的元素个数

private int size;

}

ArrayList的实现主要就是用了一个Object的数组,用来保存所有的元素,以及一个size变量用来保存当前数组中已经添加了多少元素。

public boolean add(E e){

/**

* 添加一个元素时,做了如下两步操作

* 1.判断列表的capacity容量是否足够,是否需要扩容

* 2.真正将元素放在列表的元素数组里面

*/

ensureCapacityInternal(size + 1);

elementData[size++] = e;

return true;

}

ensureCapacityInternal()的作用就是判断如果将当前的新元素加到列表后面,列表的elementData数组的大小是否满足,如果size + 1的这个需求长度大于了elementData这个数组的长度,那么就要对这个数组进行扩容。

由此看到add元素时,实际做了两个大的步骤: - 判断elementData数组容量是否满足需求 - 在elementData对应位置上设置值

这样也就出现了第一个导致线程不安全的隐患,在多个线程进行add操作时可能会导致elementData数组越界。具体逻辑如下:- 列表大小为9,即size=9

- 线程A开始进入add方法,这时它获取到size的值为9,调用ensureCapacityInternal方法进行容量判断。

- 线程B此时也进入add方法,它获取到size的值也为9,也开始调用ensureCapacityInternal方法。

- 线程A发现需求大小为10,而elementData的大小就为10,可以容纳。于是它不再扩容,返回。

- 线程B也发现需求大小为10,也可以容纳,返回。

- 线程A开始进行设置值操作, elementData[size++] = e 操作。此时size变为10。

- 线程B也开始进行设置值操作,它尝试设置elementData[10] = e,而elementData没有进行过扩容,它的下标最大为9。于是此时会报出一个数组越界的异常ArrayIndexOutOfBoundsException.

另外第二步 elementData[size++] = e 设置值的操作同样会导致线程不安全。从这儿可以看出,这步操作也不是一个原子操作,它由如下两步操作构成:- elementData[size] = e;

- size = size + 1;

在单线程执行这两条代码时没有任何问题,但是当多线程环境下执行时,可能就会发生一个线程的值覆盖另一个线程添加的值,具体逻辑如下:- 列表大小为0,即size=0

- 线程A开始添加一个元素,值为A。此时它执行第一条操作,将A放在了elementData下标为0的位置上。

- 接着线程B刚好也要开始添加一个值为B的元素,且走到了第一步操作。此时线程B获取到size的值依然为0,于是它将B也放在了elementData下标为0的位置上。

- 线程A开始将size的值增加为1 - 线程B开始将size的值增加为2

这样线程AB执行完毕后,理想中情况为size为2,elementData下标0的位置为A,下标1的位置为B。而实际情况变成了size为2,elementData下标为0的位置变成了B,下标1的位置上什么都没有。并且后续除非使用set方法修改此位置的值,否则将一直为null,因为size为2,添加元素时会从下标为2的位置上开始。

1.1.3.2 如何实现线程安全

1.1.3.2.1 synchronized关键字

在程序上加锁

1.1.3.2.2 Collections.synchronizedList()方法

假设代码为

List> data = new ArrayList<>();

为了实现线程安全,可以使用

List> data = Collections.synchronizedList(new ArrayList<>());

返回的就是一个线程安全的ArrayList类;

1.1.3.2.3 CopyOnWriteArrayList类

CopyOnWriteArrayList类所有可变操作(add, set等)都是通过对底层数组进行一次新的复制来完成的,因为在遍历时不会做任何修改,因此绝对不会抛出ConcurrentModificationException异常。

eventbus的订阅方法subscribe()里面,就采用了线程较为安全的CopyOnWriteArrayList集合

private final Map, CopyOnWriteArrayList> subscriptionsByEventType;

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {

Class> eventType = subscriberMethod.eventType;

Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);

if (subscriptions == null) {

subscriptions = new CopyOnWriteArrayList<>();

subscriptionsByEventType.put(eventType, subscriptions);

} else {

if (subscriptions.contains(newSubscription)) {

throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "

+ eventType);

}

}

1.1.4 源码

public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable

{

private static final long serialVersionUID = 8683452581122892189L;

//默认的初始容量为10

private static final int DEFAULT_CAPACITY = 10;

private static final Object[] EMPTY_ELEMENTDATA = {};

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

transient Object[] elementData;

// ArrayList中实际数据的数量

private int size;

public ArrayList(int initialCapacity) //带初始容量大小的构造函数

{

if (initialCapacity > 0) //初始容量大于0,实例化数组

{

this.elementData = new Object[initialCapacity];

}

else if (initialCapacity == 0) //初始化等于0,将空数组赋给elementData

{

this.elementData = EMPTY_ELEMENTDATA;

}

else //初始容量小于,抛异常

{

throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);

}

}

public ArrayList() //无参构造函数,默认容量为10

{

this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

}

public ArrayList(Collection extends E> c) //创建一个包含collection的ArrayList

{

elementData = c.toArray(); //返回包含c所有元素的数组

if ((size = elementData.length) != 0)

{

if (elementData.getClass() != Object[].class)

elementData = Arrays.copyOf(elementData, size, Object[].class);//复制指定数组,使elementData具有指定长度

}

else

{

//c中没有元素

this.elementData = EMPTY_ELEMENTDATA;

}

}

//将当前容量值设为当前实际元素大小

public void trimToSize()

{

modCount++;

if (size < elementData.length)

{

elementData = (size == 0)? EMPTY_ELEMENTDATA:Arrays.copyOf(elementData, size);

}

}

//将集合的capacit增加minCapacity

public void ensureCapacity(int minCapacity)

{

int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)?0: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++;

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity)

{

int oldCapacity = elementData.length;

//注意此处扩充capacity的方式是将其向右一位再加上原来的数,实际上是扩充了1.5倍

int newCapacity = oldCapacity + (oldCapacity >> 1);

if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

elementData = Arrays.copyOf(elementData, newCapacity);

}

private static int hugeCapacity(int minCapacity)

{

if (minCapacity < 0) // overflow

throw new OutOfMemoryError();

return (minCapacity > MAX_ARRAY_SIZE) ?

Integer.MAX_VALUE :

MAX_ARRAY_SIZE;

}

//返回ArrayList的大小

public int size()

{

return size;

}

//判断ArrayList是否为空

public boolean isEmpty() {

return size == 0;

}

//判断ArrayList中是否包含Object(o)

public boolean contains(Object o) {

return indexOf(o) >= 0;

}

//正向查找,返回ArrayList中元素Object(o)的索引位置

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中元素Object(o)的索引位置

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();

v.elementData = Arrays.copyOf(elementData, size);

v.modCount = 0;

return v;

}

catch (CloneNotSupportedException e) {

// this shouldn't happen, since we are Cloneable

throw new InternalError(e);

}

}

//返回一个包含ArrayList中所有元素的数组

public Object[] toArray() {

return Arrays.copyOf(elementData, size);

}

@SuppressWarnings("unchecked")

public T[] toArray(T[] a) {

if (a.length < size)

return (T[]) Arrays.copyOf(elementData, size, a.getClass());

System.arraycopy(elementData, 0, a, 0, size);

if (a.length > size)

a[size] = null;

return a;

}

@SuppressWarnings("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)

{

rangeCheck(index);

E oldValue = elementData(index);

elementData[index] = element;

return oldValue;

}

//将指定的元素添加到此列表的尾部

public boolean add(E e)

{

ensureCapacityInternal(size + 1);

elementData[size++] = e;

return true;

}

// 将element添加到ArrayList的指定位置

public void add(int index, E element) {

rangeCheckForAdd(index);

ensureCapacityInternal(size + 1);

//从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。

//arraycopy(被复制的数组, 从第几个元素开始复制, 要复制到的数组, 从第几个元素开始粘贴, 一共需要复制的元素个数)

//即在数组elementData从index位置开始,复制到index+1位置,共复制size-index个元素

System.arraycopy(elementData, index, elementData, index + 1,size - index);

elementData[index] = element;

size++;

}

//删除ArrayList指定位置的元素

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; //将原数组最后一个位置置为null

return oldValue;

}

//移除ArrayList中首次出现的指定元素(如果存在)。

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 void fastRemove(int index)

{

modCount++;

int numMoved = size - index - 1;

if (numMoved > 0)

System.arraycopy(elementData, index+1, elementData, index, numMoved);

elementData[--size] = null;

}

//清空ArrayList,将全部的元素设为null

public void clear()

{

modCount++;

for (int i = 0; i < size; i++)

elementData[i] = null;

size = 0;

}

//按照c的迭代器所返回的元素顺序,将c中的所有元素添加到此列表的尾部

public boolean addAll(Collection extends E> c) {

Object[] a = c.toArray();

int numNew = a.length;

ensureCapacityInternal(size + numNew); // Increments modCount

System.arraycopy(a, 0, elementData, size, numNew);

size += numNew;

return numNew != 0;

}

//从指定位置index开始,将指定c中的所有元素插入到此列表中

public boolean addAll(int index, Collection extends E> c) {

rangeCheckForAdd(index);

Object[] a = c.toArray();

int numNew = a.length;

ensureCapacityInternal(size + numNew); // Increments modCount

int numMoved = size - index;

if (numMoved > 0)

//先将ArrayList中从index开始的numMoved个元素移动到起始位置为index+numNew的后面去

System.arraycopy(elementData, index, elementData, index + numNew, numMoved);

//再将c中的numNew个元素复制到起始位置为index的存储空间中去

System.arraycopy(a, 0, elementData, index, numNew);

size += numNew;

return numNew != 0;

}

//删除fromIndex到toIndex之间的全部元素

protected void removeRange(int fromIndex, int toIndex)

{

modCount++;

//numMoved为删除索引后面的元素个数

int numMoved = size - toIndex;

//将删除索引后面的元素复制到以fromIndex为起始位置的存储空间中去

System.arraycopy(elementData, toIndex, elementData, fromIndex,numMoved);

int newSize = size - (toIndex-fromIndex);

//将ArrayList后面(toIndex-fromIndex)个元素置为null

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));

}

private void rangeCheckForAdd(int index)

{

if (index > size || index < 0)

throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

}

private String outOfBoundsMsg(int index) {

return "Index: "+index+", Size: "+size;

}

//删除ArrayList中包含在c中的元素

public boolean removeAll(Collection> c)

{

Objects.requireNonNull(c);

return batchRemove(c, false);

}

//删除ArrayList中除包含在c中的元素,和removeAll相反

public boolean retainAll(Collection> c)

{

Objects.requireNonNull(c); //检查指定对象是否为空

return batchRemove(c, true);

}

private boolean batchRemove(Collection> c, boolean complement) {

final Object[] elementData = this.elementData;

int r = 0, w = 0;

boolean modified = false;

try

{

for (; r < size; r++)

if (c.contains(elementData[r]) == complement) //判断c中是否有elementData[r]元素

elementData[w++] = elementData[r];

}

finally

{

if (r != size)

{

System.arraycopy(elementData, r, elementData, w, size - r);

w += size - r;

}

if (w != size)

{

// clear to let GC do its work

for (int i = w; i < size; i++)

elementData[i] = null;

modCount += size - w;

size = w;

modified = true;

}

}

return modified;

}

//将ArrayList的“容量,所有的元素值”都写入到输出流中

private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException

{

int expectedModCount = modCount;

s.defaultWriteObject();

//写入数组大小

s.writeInt(size);

//写入所有数组的元素

for (int i=0; i

s.writeObject(elementData[i]);

}

if (modCount != expectedModCount) {

throw new ConcurrentModificationException();

}

}

//先将ArrayList的“大小”读出,然后将“所有的元素值”读出

private void readObject(java.io.ObjectInputStream s)

throws java.io.IOException, ClassNotFoundException {

elementData = EMPTY_ELEMENTDATA;

s.defaultReadObject();

s.readInt(); // ignored

if (size > 0) {

// be like clone(), allocate array based upon size not capacity

ensureCapacityInternal(size);

Object[] a = elementData;

// Read in all elements in the proper order.

for (int i=0; i

a[i] = s.readObject();

}

}

}

1.1.4.1 动态数组

ArrayList是一个动态数组,实现了List, RandomAccess, Cloneable, java.io.Serializable,并允许包括Null在内的元素;

1.1.4.1.1 访问速度O(1)

实现了RandomAccess接口标识着其支持随机快速访问,RandomAccess源码什么都没有定义.因为ArrayList底层是数组,那么随机快速访问是理所当然的,访问速度O(1).

1.1.4.1.2 可以被浅复制

实现了Cloneable接口,标识着可以被复制,ArrayList中的clone()复制为浅复制; - 浅复制:只复制一个对象,但新对象和老对象是同一个地址值; - 深复制:复制一个对象,新老对象的地址值都变了。

1.1.4.2 自动扩容机制

底层使用数组实现,初始容量为10;当超出后,自动扩容为原来的1.5倍,即自动扩容机制;

数组的扩容是新建一个大容量(原始数组大小+扩充容量)的数组,然后将原始数组拷贝到新数组,然后采用的是Arrays.copyOf浅复制新数组作为扩容之后的数组,因此代价很高,尽量减小该种操作。在开始阶段,直接指定大小最好。

1.1.4.2.1 开始阶段指定容量

如果在开始能够预知要保存的元素多少,要在构造ArrayList实例时,指定容量值,避免扩容;

1.1.4.2.2 ensureCapacity

根据实际需要,调用ensureCapacity方法来手动增加ArrayList实例的容量。

1.1.4.3 Fail-Fast机制

面对并发的修改时,迭代器很快就会完全失败,报异常concurrentModificationException(并发修改一次),而不是冒着在将来某个不确定时间发生任意不确定行为的风险。

1.1.4.4 remove方法

remove方法会让下标到数组末尾的元素向前移动一个单位,并把最后一位的值置空,方便GC

ArrayList里面删除元素其实就是将该元素置位null

1.1.4.5 add(E e)

添加指定元素到末尾,如果增加的数据量很大,应使用ensureCapacity()方法,预先设置ArrayList的大小,提高初始化效率。

Object obj = new Object();

ArrayList list0 = new ArrayList();

long startTime0 = System.currentTimeMills();

for(int i = 0; i<=N; i++){

list0.add(obj);

}

long endTime0 = System.currentTimeMills();

Log.e("date", "111没有调用ensureCapacity()方法所用时间:" + (endTime0 - startTime0) + "ms");

ArrayList list1 = new ArrayList();

long startTime1 = System.currentTimeMillis();

list1.ensureCapacity(N);//预先设置list的大小

for(int i=0;i<=N;i++){

list1.add(obj);

}

long endTime1 = System.currentTimeMillis();

Log.e("date", "222调用ensureCapacity()方法所用时间:" + (endTime1 - startTime1) + "ms");

1.1.5 面试题

1.1.5.1 ArrayList的大小是如何自动增加的?你能分享一下你的代码吗?

当试图在ArrayList增加对象时,Java会检查arrayList,以确保存在的数组中有足够的容量来存储新的对象。如果没有足够容量,那么就会新建一个长度更长的数组,旧的数组就会使用Arrays.copyOf()方法被复制到新的数组中去,现有的数组引用指向了新的数组。

public boolean add(E e){

ensureCapacity(size+1);

elementData[size++] = e;

return true;

}

public void ensureCapacity(int minCapacity){

modCount++;

int oldCapacity = elementData.length;

if(minCapacity > oldCapacity){

Object oldData[] = elementData;

}

}

1.1.5.2 什么情况下你会使用ArrayList?什么时候你会选择LinkedList?

多数情况下,当访问元素比插入或者删除元素更加频繁的时候,应该使用ArrayList;

当在某个特别的索引中,插入或者删除元素更加频繁,或者不需要访问元素的时候,选择LinkedList。

原因:

ArrayList访问元素的最糟糕时间复杂度为1,而LinkedList可能就是n;在ArrayList中增加或者删除某个元素,通常会调用System.arraycpopy()方法,非常消耗资源,因此,LinkedList会更好一些。

1.1.5.3 当传递ArrayList到某个方法中,或者某个方法返回ArrayList,什么时候要考虑安全隐患?如何修复安全违规这个问题呢?

当array被当做参数传递到某个方法中,如果array在没有被复制的情况下直接被分配给了成员变量,那么就可能发生这种情况,即当原始的数组被调用的方法改变的时候,传递到这个方法中的数组也会改变。下面的这段代码展示的就是安全违规以及如何修复这个问题。

ArrayList被直接赋值给成员变量——安全隐患

public void setMyArray(String[] myArray){

this.myArray = myArray;

}

修复

public void setMyArray(String[] newMyArray){

if(newMyArray == null){

this.myArray = new String[0];

} else{

this.myArray = Arrays.copyOf(newMyArray, newMyArray.length);

}

}

1.1.5.4 如何复制某个ArrayList到另一个Arraylist中去?

1.使用clone()方法,如

ArrayList newArray = oldArray.clone();

2.使用ArrayList构造方法,比如

ArrayList myObject = new ArrayList(myTempObject);

3.使用Collection的copy方法

1.1.6 概述

1.ArrayList是List接口的可变数组非同步实现,并允许包括null在内的所有元素;

2.底层使用数组实现;

3.该集合是可变长度数组,数组扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量增长大约是其容量的1.5倍,这种操作的代价很高。

4.采用了Fail-Fast机制,面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险

5.remove方法会让下标到数组末尾的元素向前移动一个单位,并把最后一位的值置空,方便GC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值