【JAVA SE】集合框架--ArrayList

ArrayList继承了 AbstractList 类,实现了collection接口。ArrayList顾名思义 ,数组列表的意思。

一.常用api使用

1.add(Object obj);
add方法用于向list中添加对象,将指定元素追加到list末尾 。接收参数类型为Object类型对象,所以可以向ArrayList中添加任意类型的对象,同一个list中可以放入不同类型的对象。但是list中不能放入原生数据类型。
2.get(int index);
get方法用于从list中取出对象。接收参数为list中数组下标。根据对象在list中的位置取出对应对象。方法返回一个Objec类型对象,使用时需要将返回的对象转为目标类型的对象。
向list中add的对象和取出时接受的对象如果不是同一类型的对象,编译时并不会报错。如下代码:
List list=new ArrayList();
list.add("hello");
Integer hello=(Integer)list.get(0);
如果向list中add一个String类型的对象,取出时转换为Integer类型对象,编译时并不会报错,因为编译器只知道get()方法返回的是一个Object类型的对象,并不知道实际上是什么对象,只有运行时才回报错。
3.clear();
将list清空。
4.isEmpty();
判断list是否为空,返回一个boolean值。
5.remove(int index)
清除指定位置的数组元素。参数为下标位置,返回一个boolean值。如果list中存在该对象,返回true。
6.toArray()
将集合转化为一个数组。
 
List list=new ArrayList();
list.add(new Integer(1));
list.add(new Integer(2));
list.add(new String("hello"))
Object[] obj=list.toArray();
Integer[] in=(Integer[])list.toArray();这种情况下必然是不可能直接转换为Integer类型的数组。


二.ArrayList实现原理

1.集合中存放的不是对象本身,而是对象的引用。
2.ArrayList底层实现一个数组,当使用不带参数的构造方法时,会生成一个Object类型的空数组。


查看源代码
/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
将一个空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA  赋值给ArrayList 负责存放对象的数组
elementData

/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};


/**
 * The array buffer into which the elements of the ArrayList are stored.
 * The capacity of the ArrayList is the length of this array buffer. Any
 * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
 * will be expanded to DEFAULT_CAPACITY when the first element is added.
 */

transient Object[] elementData; // non-private to simplify nested class access


3.add方法实现
查看源代码
/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return <tt>true</tt> (as specified by {@link Collection#add})
 */
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
根据文档,add方法是将指定元素追加到list的末尾
返回一个boolean值,元素添加成功返回true,否则返回false;
然后看这行代码 ensureCapacityInternal(size + 1);  // Increments modCount!! 
确保容量内部(原谅蹩脚的英语) 参数为:size+1,看下size是什么
/**
 * The size of the ArrayList (the number of elements it contains).
 *
 * @serial
 */
private int size;
根据官方注释size of the ArrayList ,size为list中元素实际个数。然后看
ensureCapacityInternal方法的实现
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
calculateCapacity 方法的实现
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
判断list存放元素的数组elementData 长度是否为0。如果值为0,返回
DEFAULT_CAPACITY和minCapacity中较大的值,
/**
 * Default initial capacity.
 */
private static final int DEFAULT_CAPACITY = 10;
从源码中可以看出  DEFAULT_CAPACITY 为默认初始容量 10 ,minCapacity是之前由构造方法传入的size+1,我们第一次调用add方法时值为1,所以
calculateCapacity的返回值为10。
继续看ensureExplicitCapacity 方法实现
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;


    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
此时我们已经确定minCapacity值为10,elementData.length 为0,所以if为真,继续看grow方法的实现
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    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);
}
此时int oldCapacity = elementData.length; 为0。minCapacity 为10,所以
if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;成立
elementData = Arrays.copyOf(elementData, newCapacity);将长度为0的数组复制到一个长度为10的数组中。
     到此,add方法中的ensureCapacityInternal 方法看完了 ,继续看add方法,elementData[size++] = e; 将目标对象添加到数组中,再将数组实际长度加1,然后返回true。
    总结下add方法 。首先判断list中实际元素个数是否为0,如果为0将数据容量扩容到10,并向数组开始位置插入目标元素。
如果list中实际元素个数小于10且大于0,则不对数组进行扩容,直接将目标元素追加到list末尾
当list中实际长度为10,再调用add方法向其中追加元素,先对数组进行扩容
int newCapacity = oldCapacity + (oldCapacity >> 1);
此时 oldCapacity 旧的容量是10 ,新容量newCapacity 为15,将旧的数组elementData 复制到新的长度为15的
elementData 中去,最后向list末尾追加元素。
当list中实际元素数量到达数组容量时再进行扩容。
4.get方法原理
/**
 * Returns the element at the specified position in this list.
 *
 * @param  index index of the element to return
 * @return the element at the specified position in this list
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E get(int index) {
    rangeCheck(index);


    return elementData(index);
}
get方法 很简单,先查询index是否不在范围之内(是否为负数,或者超过了list的实际元素个数-1),返回数组中的对应元素


5.remove方法实现
/**
 * Removes the element at the specified position in this list.
 * Shifts any subsequent elements to the left (subtracts one from their
 * indices).
 *
 * @param index the index of the element to be removed
 * @return the element that was removed from the list
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
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; // clear to let GC do its work
list.add(new Integer(2));
Object[] obj=list.toArray();
toArray方法返回的是一个Object类型的数组
这里不能将返回的数组直接转换为Integer类型的数组 。
Integer[] in=(Integer[])list.toArray(); 这样写是错误的。
因为toArray方法只知道返回的是一个Object类型的数组,数组中每一个元素都是Object类型的对象,只有循环到某个特定元素时才知道这个对象的具体类型。
所以数组中既可以有Integer类型的对象,又可以有String类型的对象。
List list=new ArrayList();
list.add(new Integer(1));
 return oldValue;
}
remove方法的实现实际上是将原数组除去要移除的元素之外拷贝的新数组,新数组长度不变,所有在移除元素之后的元素统一向前移位。

将新数组中最后一位设为null,并将list长度-1。所以list删除元素的代价是相当大的,如果删除元素位置靠前将有大量移位操作


ps:开始写博客了,一方面巩固下基础,一方面锻炼下总结能力。不对的地方请指出,欢迎讨论。

联系方式qq:29138386

本文为原创,转载请说明,谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值