标签


Java容器ArrayList

2015年02月01日

容器对null的特殊处理

AbstractCollection实现Collection接口,允许包含null元素,在contains()和remove()方法中需要对null进行特殊处理,不能简单调用equal()方法进行比较。 ArrayList和LinkedList继承AbstractList,AbstractList继承AbstractCollection。

public boolean contains(Object o) {
    Iterator<E> it = iterator();
    if (o==null) {
        while (it.hasNext())
            if (it.next()==null)
                return true;
    } else {
        while (it.hasNext())
            if (o.equals(it.next()))
                return true;
    }
    return false;
}

public boolean remove(Object o) {
    Iterator<E> it = iterator();
    if (o==null) {
        while (it.hasNext()) {
            if (it.next()==null) {
                it.remove();
                return true;
            }
        }
    } else {
        while (it.hasNext()) {
            if (o.equals(it.next())) {
                it.remove();
                return true;
            }
        }
    }
    return false;
}    

ArrayList.indexOf()/lastIndexOf()很多方法都对null进行了特殊的处理。 AbstractMap.containsValue()/containsKey()/get()/remove()等一系列方法也对null进行了处理。

ArrayList

ArrayList默认元素数组大小为10。

private static final int DEFAULT_CAPACITY = 10;

看过ArrayList源码的也许对底层存储的数组elementData前面加了transient关键字很疑惑,这样数组怎么序列化? 因为变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。

private transient Object[] elementData;

ArrayList重写了writeObject()/readObject()方法,自定义了序列化行为。 从代码中可以看到,ArrayList并没有把整个elementData数组的元素都进行了序列化,而只序列化了size个元素,仅序列化实际存储的元素,而不是整个数组,这样能有效的节省空间。elementData是一个缓存数组。

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}    

ArrayList进行扩展时,通常已1.5倍的大小进行扩展(需要考虑最大,最小元素个数)。

int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);

与ArrayList类似,LinkedList也重写了writeObject()/readObject()方法,LinkedList使用双向循环链表存储元素。

transient int size = 0;
transient Node<E> first;
transient Node<E> last;