java.util.list 赋值_java.util(ArrayList)

public class ArrayList extends AbstractList

implements List, RandomAccess, Cloneable, java.io.Serializable

{private static final int DEFAULT_CAPACITY = 10; //ArrayList中Object[]中的默认初始容量

private static final Object[] EMPTY_ELEMENTDATA = {}; //空Object[]数组对象

private transient Object[] elementData; //定义了一个私有的未被序列化的数组elementData,用来存储ArrayList的对象列表

private int size; //ArrayList中实际数据的数量

public ArrayList(int initialCapacity) { //带容量的构造函数

super();if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);this.elementData = newObject[initialCapacity];

}public ArrayList() { //无参构造函数,默认容量为10,这里的1暂时还没有设置,在add(E)的时候会指定

super();this.elementData =EMPTY_ELEMENTDATA;

}public booleanadd(E e) {//确保数组容量足够添加元素进入数组

ensureCapacityInternal(size + 1); //Increments modCount!!

elementData[size++] =e;return true;

}private void ensureCapacityInternal(intminCapacity) {if (elementData ==EMPTY_ELEMENTDATA) {

minCapacity=Math.max(DEFAULT_CAPACITY, minCapacity);

}

ensureExplicitCapacity(minCapacity);

}private void ensureExplicitCapacity(intminCapacity) {

modCount++;//overflow-conscious code

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}private void grow(intminCapacity) {//overflow-conscious code

int oldCapacity =elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1); //10->16->25->38->58->88->...

if (newCapacity - minCapacity < 0)

newCapacity=minCapacity;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);//意思就是把原数组内容复制到行数组上,容量变大了,将引用赋给elementData

}private static int hugeCapacity(intminCapacity) {if (minCapacity < 0) //overflow

throw newOutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :

MAX_ARRAY_SIZE;

}

以上是jdk1.7的描述,结论如下:

ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长;

ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。

ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Cloneable接口,能被克隆。

注意扩充容量的方法ensureCapacity。ArrayList在每次增加元素(可能是1个,也可能是一组)时,都要调用该方法来确保足够的容量。当容量不足以容纳当前的元素个数时,就设置新的容量为旧的容量的1.5倍加1,如果设置后的新容量还不够,则直接新容量设置为传入的参数(也就是所需的容量),而后用Arrays.copyof()方法将元素拷贝到新的数组(详见下面的第3点)。从中可以看出,当容量不够时,每次增加元素,都要将原来的元素拷贝到一个新的数组中,非常之耗时,也因此建议在事先能确定元素数量的情况下,才使用ArrayList,否则建议使用LinkedList。

ArrayList的数组扩容调用了Arrays.copyof(),该方法实际上是在其内部又创建了一个长度为newlength的数组,调用System.arraycopy()方法,将原来数组中的元素复制到了新的数组中。System.arraycopy()方法。该方法被标记了native,调用了系统的C/C++代码,在JDK中是看不到的,但在openJDK中可以看到其源码。该函数实际上最终调用了C语言的memmove()函数,因此它可以保证同一个数组内元素的正确复制和移动,比一般的复制方法的实现效率要高很多,很适合用来批量处理数组。Java强烈推荐在复制大量数组元素时用该方法,以取得更高的效率。

多线程安全问题分析

private transient Object[] elementData; //定义了一个私有的未被序列化的数组elementData,用来存储ArrayList的对象列表

ArrayList内部是使用数组保存元素的,在ArrayList中此数组即是共享资源,当多线程对此数据进行操作的时候如果不进行同步控制,即有可能会出现线程安全问题。

一: add方法可能出现的问题分析

首先我们看一下add的源码如下:

public booleanadd(E e) {

ensureCapacityInternal(size+ 1); //是否需要扩容

elementData[size++] = e;//赋值

return true;

}

1:下标越界问题

多个线程进入ensureCapacityInternal()并执行完毕,此时都不需要扩容,依次赋值时会size+1,所以从第二个开始的线程赋值时其下标很可能超过了容量值,赋值时就报错了

2:存入的值变为null

elementData[size++] = e是先赋值再size+1,多线程运行到赋值还没+1时,size位置上被覆盖了多次,然后多次+1,size+1,+2等位置没赋值过,下次就直接从size+n开始赋值,看起来就add了null值一样,此时不会报错,因为add时没有null所以取出时没做考虑就可能报NullPointerException了.

3.数据个数小于预期值

在多线程操作下 size++不是原子操作,会出现最终数据元素个数小于期望值。

代码验证

importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;public classTest4 {private static List list = new ArrayList();private static ExecutorService executorService = Executors.newFixedThreadPool(1000);private static class IncreaseTask extendsThread{

@Overridepublic voidrun() {

System.out.println("ThreadId:" + Thread.currentThread().getId() + " start!");for(int i =0; i < 100; i++){

list.add(i);

}

System.out.println("ThreadId:" + Thread.currentThread().getId() + " finished!");

}

}public static voidmain(String[] args) {for(int i=0; i < 1000; i++){

executorService.submit(newIncreaseTask());

}

executorService.shutdown();while (!executorService.isTerminated()){try{

Thread.sleep(1000*10);

}catch(InterruptedException e){

e.printStackTrace();

}

}

System.out.println("All task finished!");

System.out.println("list size is :" +list.size());

}

}

867d10b9367638727a84624ff60e60a1.png

从以上执行结果来看,最后输出的结果会小于我们的期望值。即当多线程调用add方法的时候会出现元素覆盖的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值