1.介绍
ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。数组元素的类型为Object类型,对ArrayList的所有操作底层都是基于数组的。
ArrayList 继承了 AbstractList ,并实现了 List 接口。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- E: 泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型。
当不设置E时,ArrayList里可以存储不同的引用数据类型
ArrayList q = new ArrayList();
q.add(1);//自动装箱为 Integer
q.add(2);
q.add(3);
q.add("qqqq");
System.out.println(q);
但设置E后只能是对应类型
ArrayList是线程不安全的
对ArrayList进行添加元素的操作的时候是分两个步骤进行的:
- 先在
object[size]的位置上存放需要添加的元素; - 将
size的值增加1。
由于这个过程在多线程的环境下是不能保证具有原子性 的,因此ArrayList在多线程的环境下是线程不安全的。
在单线程运行的情况下,如果Size = 0,添加一个元素后,此元素在位置 0,而且Size=1;而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此ArrayList 添加元素,因为此时 Size 仍然等于 0
请注意,此实现不同步。 如果多个线程同时访问
ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。 (结构修改是添加或删除一个或多个元素的任何操作,或显式调整后备数组的大小;仅设置元素的值不是结构修改。)这通常通过同步一些自然封装的对象来实现。 如果不存在此类对象,则应使用Collections.synchronizedList方法“包装”该列表。 这最好在创建时完成,以防止意外地不同步访问列表:
List list = Collections.synchronizedList(new ArrayList(...));
2.构造方法
| 构造器 | 描述 |
|---|---|
| ArrayList() | 构造一个初始容量为10的空列表。 |
| ArrayList(int initialCapacity) | 构造具有指定初始容量的空列表。 |
| ArrayList(Collection<? extends E> c) | 按照集合的迭代器返回的顺序构造一个包含指定集合元素的列表。 |
//在添加元素后才初始化,一开始时赋值的是
//private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//通过一开始都赋值为final数组,可以在添加元素前减少直接new大量Object数组的消耗
//关于为什么初始容量是10见后面扩容
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//大于0时直接通过指定参数生成数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//等于0时统一赋值为固定的final数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
//负数时抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
ArrayList q = new ArrayList();
q.add(4);
q.add(1);
q.add(88);
q.add("ww");
ArrayList w=new ArrayList(q);
System.out.print(w);
3.基本方法
- add() 添加元素
ArrayList q = new ArrayList();
q.add(1);
System.out.println(q);
添加的元素可以为null
- get() 访问元素
因为ArrayList实现的是List接口,里面的元素是有序的,可以通过索引获取元素
ArrayList q = new ArrayList();
q.add("q");
q.add("w");
System.out.println(q.get(0));
System.out.println(q.get(1));
- set() 修改元素
ArrayList q = new ArrayList();
q.add("q");
q.add("w");
System.out.println(q);
q.set(1,"r");
System.out.println(q);
- remove() 删除元素
remove的参数可以是索引或者存储的类(Object),第二种只会删除第一个元素
ArrayList q = new ArrayList();
q.add("q");
q.add("w");
q.add("q");
q.add("w");
System.out.println(q);
q.remove(2);//索引
q.remove("w");//Object
System.out.println(q);
- size() 计算大小
ArrayList q = new ArrayList();
q.add("q");
q.add("w");
q.add("q");
q.add("w");
System.out.println(q.size());
- 使用 for-each 来迭代元素
ArrayList q = new ArrayList();
q.add("q");
q.add("w");
q.add("q");
q.add("w");
for(Object i:q)//若果声明了泛型E,用相应类型声明i即可
{
System.out.println(i);
}
- Collections.sort()排序
ArrayList q = new ArrayList();
q.add("q");
q.add("w");
q.add("q");
q.add("w");
Collections.sort(q);
System.out.println(q);
也可以对Integer排序
ArrayList q = new ArrayList();
q.add(4);
q.add(1);
q.add(88);
q.add(4);
System.out.println(q);
Collections.sort(q);
System.out.println(q);
- 其他常用方法
无法复制加载中的内容
4.迭代器
ArrayList q = new ArrayList();
q.add(4);
q.add(1);
q.add(88);
q.add(4);
Iterator i = q.iterator();
while (i.hasNext()) {
System.out.print(" "+i.next());
}
此类的
iterator和listIterator方法返回的迭代器是快速失败的 :如果在创建迭代器之后的任何时候对列表进行结构修改,迭代器将抛出ConcurrentModificationException。
请注意,迭代器的快速失败行为无法得到保证,因为一般来说,在存在不同步的并发修改时,不可能做出任何硬性保证。失败快速迭代器以尽力而为的方式抛出
ConcurrentModificationException。因此,编写依赖于此异常的程序以确保其正确性是错误的: 迭代器的快速失败行为应该仅用于检测错误。
在生成迭代器后修改结构
5.扩容
数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。
扩容正常会发在add()添加元素时候,因此通过add()的过程了解扩容
public boolean add(E e) {
modCount++;
add(e, elementData, size);//真正添加过程
return true;//成功添加返回true
}
private void add(E e, Object[] elementData, int s) {//s为size
if (s == elementData.length)//当添加e的下标大于数组下标时触发扩容
elementData = grow();
elementData[s] = e;//容量够时,将e加入对应下标
size = s + 1;//长度加一
}
private Object[] grow() {
return grow(size + 1);//size为要存储的下标,因此传递给最小容量时加一
}
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//oldCapacity大于0证明已经初始化过,不等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA说明不是
//通过无参构造方法创建,那么扩容就是创建新数组后把旧数组拷贝过去
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
//对无参构造方法创建的数组,第一次添加即为初始化,且从最小容量和DEFAULT_CAPACITY(10)
//取大者,单个添加即初始化为10
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
将此ArrayList实例的容量调整为列表的当前大小。应用程序可以使用此操作来最小化ArrayList实例的存储。
public void ensureCapacity(int minCapacity) {
if (minCapacity > elementData.length
&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
&& minCapacity <= DEFAULT_CAPACITY)) {
modCount++;
grow(minCapacity);
}
}
增加此ArrayList实例的容量,以确保它至少可以容纳由minimumcapacity参数指定的元素数。