ArrayList

125 阅读5分钟

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进行添加元素的操作的时候是分两个步骤进行的:

  1. 先在object[size]的位置上存放需要添加的元素;
  2. 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.基本方法

  1. add() 添加元素
ArrayList q = new ArrayList();

q.add(1);

System.out.println(q);

添加的元素可以为null

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

  1. set() 修改元素
ArrayList q = new ArrayList();

q.add("q");

q.add("w");

System.out.println(q);

q.set(1,"r");

System.out.println(q);

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

  1. size() 计算大小
ArrayList q = new ArrayList();

q.add("q");

q.add("w");

q.add("q");

q.add("w");

System.out.println(q.size());

  1. 使用 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);

}

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

  1. 其他常用方法

无法复制加载中的内容

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

}

此类的iteratorlistIterator方法返回的迭代器是快速失败的 :如果在创建迭代器之后的任何时候对列表进行结构修改,迭代器将抛出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参数指定的元素数。