ArrayList源码分析

ArrayList源码分析

继承实现

public class ArrayList<E> extends AbstractList<E> implements Cloneable, Serializable, RandomAccess

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>

public interface List<E> extends Collection<E>

public abstract class AbstractCollection<E> implements Collection<E>

public interface Collection<E> extends Iterable<E>
  • Iterable接口只有一个方法iterator(),返回一个迭代器
  • Java 的集合主要按两种接口分类:CollectionMap

    • Collection接口实现Iterable接口,作为集合的一个根接口,定义了一组对象和它的子类需要实现的方法:
    • add()addAll()clear()contains()containsAll()equals()hashCode()isEmpty()iterator()remove()removeAll()retainAll()size()toArray()
  • AbstractCollection抽象类实现Collection接口,并对一些方法进行实现。有iterator()size()两个抽象方法,其子类必须实现

  • AbstractCollectionJava 集合框架中 Collection 接口 的一个直接实现类Collection 下一系的大多数子类都继承 AbstractCollection ,比如 List 的实现类, Set的实现类。
  • List接口继承Collection接口,定义了一系列更加适合线性集合的方法。是一个元素有序的可以重复可以为 null 的集合。最常使用的几种 List 实现类是 ArrayListLinkedListVector
  • AbstractList 继承自 AbstractCollection 抽象类,实现了 List 接口 ,是 ArrayListAbstractSequentiaList 的父类。

重要属性

说实话,ArrayList的属性没啥!

// 容量增量。如果数组容量满了,增加如下大小
private static final int MIN_CAPACITY_INCREMENT = 12;
// 数组的大小
int size;
// 真实保存数据的数组
transient Object[] array;

transient不会参与序列化

构造方法

ArrayList有3个构造方法:

1.无参构造

public ArrayList() {
    array = EmptyArray.OBJECT;
}

public static final Object[] OBJECT = new Object[0];

无参构造是直接实例化了一个静态final数组。size=0.

2.容量构造

public ArrayList(int capacity) {
    if (capacity < 0) {
        throw new IllegalArgumentException("capacity < 0: " + capacity);
    }
    array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
}

先检查容量。

如果容量为0,那么跟无参构造一样。

如果>0,就new Object[capacity]

3.集合构造

public ArrayList(Collection<? extends E> collection) {
    if (collection == null) {
        throw new NullPointerException("collection == null");
    }

    Object[] a = collection.toArray();
    if (a.getClass() != Object[].class) {
        Object[] newArray = new Object[a.length];
        System.arraycopy(a, 0, newArray, 0, a.length);
        a = newArray;
    }
    array = a;
    size = a.length;
}

这个构造是通过将新集合的数据拷贝到该集合中。

首先,Collection是一个接口,所有的集合:包括List, Set, Map, Vector等都实现了这个接口。接口内容有:增、删、改、查、迭代器、是否包括、是否为空等等。

toArray()转成Object[],然后判断a.getClass()

如果集合是new ArrayList()生成的,那么Class就是ArrayList

拷贝方式:先new一个同等大小的数组,然后System.arraycopy()数据,最后赋值给a

刷新arraysize

功能函数

add

add方法有两个

@Override public boolean add(E object) {
    Object[] a = array;
    int s = size;
    if (s == a.length) {
        Object[] newArray = new Object[s +
                (s < (MIN_CAPACITY_INCREMENT / 2) ?
                 MIN_CAPACITY_INCREMENT : s >> 1)];
        System.arraycopy(a, 0, newArray, 0, s);
        array = a = newArray;
    }
    a[s] = object;
    size = s + 1;
    modCount++;
    return true;
}

临时变量是一个好习惯

判断了数组的大小集合的大小是否一致,如果一致代表数组已满。

满了就要重新分配空间,newArray就是重新分配空间的数组。

分配规则:如果容量增量的一半大于当前数组长度,就增量小点。增量MIN_CAPACITY_INCREMENT。如果不大于,就增量MIN_CAPACITY_INCREMENT的2倍。然后得到的大小再加上当前数组的长度,就是newArray的长度。

随后,拷贝数据到newArray中。给size号位置添加元素。

刷新数组和size

至于modCount不用管,这个东西是你无论增删查改都会增加的一个计量数。

第2个add方法

这个方法比第1个add方法略复杂,因为它是指定位置的插入元素。

@Override public void add(int index, E object) {
    Object[] a = array;
    int s = size;
    if (index > s || index < 0) {
        throwIndexOutOfBoundsException(index, s);
    }

    if (s < a.length) {
        System.arraycopy(a, index, a, index + 1, s - index);
    } else {
        // assert s == a.length;
        Object[] newArray = new Object[newCapacity(s)];
        System.arraycopy(a, 0, newArray, 0, index);
        System.arraycopy(a, index, newArray, index + 1, s - index);
        array = a = newArray;
    }
    a[index] = object;
    size = s + 1;
    modCount++;
}

先是赋值和判断。

接着判断,如果集合大小小于数组大小。那么就说明数组有空间插入,直接System.arraycopy()拷贝元素,从index开始拷贝。以便留坑给object

如果不小于,说明数组已满。得扩容。newCapacity()就是一个扩容方法

private static int newCapacity(int currentCapacity) {
    int increment = (currentCapacity < (MIN_CAPACITY_INCREMENT / 2) ?
            MIN_CAPACITY_INCREMENT : currentCapacity >> 1);
    return currentCapacity + increment;
}

可以看到,扩容规则和第1个add方法的扩容规则完全一样!只不过这里单独提取出来了而已。

newArray新数组,然后System.arraycopy()分段拷贝2次。第1次从0拷贝到index,第2次从index拷贝到最后。留坑给object

刷新数组和size。

addAll

两个addAll方法

第1个addAll()

@Override public boolean addAll(Collection<? extends E> collection) {
    Object[] newPart = collection.toArray();
    int newPartSize = newPart.length;
    if (newPartSize == 0) {
        return false;
    }
    Object[] a = array;
    int s = size;
    int newSize = s + newPartSize; // If add overflows, arraycopy will fail
    if (newSize > a.length) {
        int newCapacity = newCapacity(newSize - 1);  // ~33% growth room
        Object[] newArray = new Object[newCapacity];
        System.arraycopy(a, 0, newArray, 0, s);
        array = a = newArray;
    }
    System.arraycopy(newPart, 0, a, s, newPartSize);
    size = newSize;
    modCount++;
    return true;
}

同样的赋值和判断。

得到当前集合和新数组的总长度newSize

如果newSize大于当前数组的长度,就得扩容。而扩容方式,知道了add就知道了。

这个是newArray新数组,先拷贝当前集合的数据。之后,再拷贝新数组的数据。

刷新数组和size

第2个addAll()方法

@Override
public boolean addAll(int index, Collection<? extends E> collection) {
    int s = size;
    if (index > s || index < 0) {
        throwIndexOutOfBoundsException(index, s);
    }
    Object[] newPart = collection.toArray();
    int newPartSize = newPart.length;
    if (newPartSize == 0) {
        return false;
    }
    Object[] a = array;
    int newSize = s + newPartSize; // If add overflows, arraycopy will fail
    if (newSize <= a.length) {
         System.arraycopy(a, index, a, index + newPartSize, s - index);
    } else {
        int newCapacity = newCapacity(newSize - 1);  // ~33% growth room
        Object[] newArray = new Object[newCapacity];
        System.arraycopy(a, 0, newArray, 0, index);
        System.arraycopy(a, index, newArray, index + newPartSize, s-index);
        array = a = newArray;
    }
    System.arraycopy(newPart, 0, a, index, newPartSize);
    size = newSize;
    modCount++;
    return true;
}

这个也很好理解了!

如果newSize小于等于当前数组长度,直接分段拷贝数据。

如果大于,就newArray,再分段拷贝数据。

clear

@Override public void clear() {
    if (size != 0) {
        Arrays.fill(array, 0, size, null);
        size = 0;
        modCount++;
    }
}

如果有数据,先将所有元素置为0.

再将size置为0.

clone

@Override public Object clone() {
    try {
        ArrayList<?> result = (ArrayList<?>) super.clone();
        result.array = array.clone();
        return result;
    } catch (CloneNotSupportedException e) {
       throw new AssertionError();
    }
}

clone新的ArrayList

然后将当前集合的array数组赋值给新集合的array数组。

最后返回新集合。

ensureCapacity

public void ensureCapacity(int minimumCapacity) {
    Object[] a = array;
    if (a.length < minimumCapacity) {
        Object[] newArray = new Object[minimumCapacity];
        System.arraycopy(a, 0, newArray, 0, size);
        array = newArray;
        modCount++;
    }
}

这个方法是确保容量的方法。

如果当前真实数组的长度小于你指定的长度,那么就以你指定的长度重新创建数组,拷贝数据。最后刷新数组。

get size isEmpty

@SuppressWarnings("unchecked") @Override public E get(int index) {
    if (index >= size) {
        throwIndexOutOfBoundsException(index, size);
    }
    return (E) array[index];
}

@Override public int size() {
    return size;
}

@Override public boolean isEmpty() {
    return size == 0;
}

get没啥好说的,直接返回数组元素!

另外两个也一样。

contains indexOf lastIndexOf

@Override public boolean contains(Object object) {
    Object[] a = array;
    int s = size;
    if (object != null) {
        for (int i = 0; i < s; i++) {
            if (object.equals(a[i])) {
                return true;
            }
        }
    } else {
        for (int i = 0; i < s; i++) {
            if (a[i] == null) {
                return true;
            }
        }
    }
    return false;
}

@Override public int indexOf(Object object) {
    Object[] a = array;
    int s = size;
    if (object != null) {
        for (int i = 0; i < s; i++) {
            if (object.equals(a[i])) {
                return i;
            }
        }
    } else {
        for (int i = 0; i < s; i++) {
            if (a[i] == null) {
                return i;
            }
        }
    }
    return -1;
}

@Override public int lastIndexOf(Object object) {
    Object[] a = array;
    if (object != null) {
        for (int i = size - 1; i >= 0; i--) {
            if (object.equals(a[i])) {
                return i;
            }
        }
    } else {
        for (int i = size - 1; i >= 0; i--) {
            if (a[i] == null) {
                return i;
            }
        }
    }
    return -1;
}

这3个放一起讲了,因为这3个很类似。

都是直接for循环,从0开始到最后或者从最后开始到0遍历。挨个比较!

有趣的是,null也是一种元素。也参与了比较。

remove

@Override public E remove(int index) {
    Object[] a = array;
    int s = size;
    if (index >= s) {
        throwIndexOutOfBoundsException(index, s);
    }
    @SuppressWarnings("unchecked") E result = (E) a[index];
    System.arraycopy(a, index + 1, a, index, --s - index);
    a[s] = null;  // Prevent memory leak
    size = s;
    modCount++;
    return result;
}

@Override public boolean remove(Object object) {
    Object[] a = array;
    int s = size;
    if (object != null) {
        for (int i = 0; i < s; i++) {
            if (object.equals(a[i])) {
                System.arraycopy(a, i + 1, a, i, --s - i);
                a[s] = null;  // Prevent memory leak
                size = s;
                modCount++;
                return true;
            }
        }
    } else {
        for (int i = 0; i < s; i++) {
            if (a[i] == null) {
                System.arraycopy(a, i + 1, a, i, --s - i);
                a[s] = null;  // Prevent memory leak
                size = s;
                modCount++;
                return true;
            }
        }
    }
    return false;
}

第1个remove方法,通过位置删除:

判断范围。

index位置的元素给result。然后将index+1开始到最后的这段元素整体前移1位。

size位置为null。刷新size并返回result

第2个remove方法,通过数据删除:

contains一样的寻找方式,和第1个remove一样的删除方式!

removeRange

@Override public E remove(int index) {
    Object[] a = array;
    int s = size;
    if (index >= s) {
        throwIndexOutOfBoundsException(index, s);
    }
    @SuppressWarnings("unchecked") E result = (E) a[index];
    System.arraycopy(a, index + 1, a, index, --s - index);
    a[s] = null;  // Prevent memory leak
    size = s;
    modCount++;
    return result;
}

@Override public boolean remove(Object object) {
    Object[] a = array;
    int s = size;
    if (object != null) {
        for (int i = 0; i < s; i++) {
            if (object.equals(a[i])) {
                System.arraycopy(a, i + 1, a, i, --s - i);
                a[s] = null;  // Prevent memory leak
                size = s;
                modCount++;
                return true;
            }
        }
    } else {
        for (int i = 0; i < s; i++) {
            if (a[i] == null) {
                System.arraycopy(a, i + 1, a, i, --s - i);
                a[s] = null;  // Prevent memory leak
                size = s;
                modCount++;
                return true;
            }
        }
    }
    return false;
}

这是删除一段元素的方法。

fromIndextoIndex这一段的元素删除。

方式:将toIndex开始往后的数据拷贝到fromIndex位置开始往后。

剩余rangeSize个位置,应该置为0.

fill填充0。刷新size。

set

@Override public E set(int index, E object) {
    Object[] a = array;
    if (index >= size) {
        throwIndexOutOfBoundsException(index, size);
    }
    @SuppressWarnings("unchecked") E result = (E) a[index];
    a[index] = object;
    return result;
}

easy了!

直接给数组元素赋值。

toArray

@Override public Object[] toArray() {
    int s = size;
    Object[] result = new Object[s];
    System.arraycopy(array, 0, result, 0, s);
    return result;
}

拷贝数据到新创建的数组中并返回!

Other

剩下的主要就是迭代器了。hasNextnext之类的。还有readObjectwriteObject等一些不重要的方法了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值