Parsing the set type source java ArrayList

Foreword

As an old farm yard, not only to talk about architecture, about concurrency, we can not forget the most basic language and data structures, and are therefore open up this series of articles, written a month for one to two articles on the basics of java to Reviewing the Old.

If there is no special, java version of this series of articles are used 1.8.0.

Of course, the first to talk ArrayList, because this is the java list the most common type of collection, it is used internally as an array of storage space, it can grow automatically when adding elements. Overall, ArrayList is relatively simple to achieve, not listed here, it's all the code, just look at some interesting places.

Member variables

    private static final int DEFAULT_CAPACITY = 10;
    private static final Object[] EMPTY_ELEMENTDATA = {};
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    transient Object[] elementData; 
    private int size;
  • DEFAULT_CAPACITY constant, the initial words of time if you do not specify capacity, use this value;
  • EMPTY_ELEMENTDATA empty array, all of the empty ArrayList elementData can share this value, avoiding the use of null;
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA also elementData value, representing the initial capacity storage space to the default value (non-zero), but had no actual elements, increase the storage space needed to wait, and EMPTY_ELEMENTDATA strategy is not the same;
  • elementData array provides storage space;
  • length size list, list of elements stored in elementData [0 ~ size).

PS: To be honest, in the end use of DEFAULTCAPACITY_EMPTY_ELEMENTDATA brought any good, I did not understand.

initialization

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        this.elementData = EMPTY_ELEMENTDATA;
    }
}
  • The first, designated initial capacity;
  • Second, the default capacity, at this time DEFAULTCAPACITY_EMPTY_ELEMENTDATA handy;
  • Third, using another set of initialization, using the method set toArray, is worth noting that, toArray not necessarily return the collection type Object [].

Room to grow

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

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

These are the three private methods

  • ensureCapacityInternal, increase capacity before adding a layer of inspection, if a elementData DEFAULTCAPACITY_EMPTY_ELEMENTDATA, ensuring capacity not less than the default capacity;
  • ensureExplicitCapacity, capacity check whether indeed the need to expand the array;
  • grow, execute an array expansion, expansion of the first half to try to see if needs; that is to say the expansion space, not in strict accordance with the input parameters.

There is a doubt, the phrase ensureExplicitCapacity method modCount++Why would perform outside the if statement.

CRUD

There are many basic operations listed here only a few representative.

1. Find:

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

ArrayList find elements of the process is to iterate, but also the null can find.

2, insert:

public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

A range check before inserting made, and to ensure the capacity, and finally all the elements after insertion of a position and then further move; the use of removable memory System.arraycopy, which is a native method .

It is noteworthy that, add method does not directly call modCount ++, because there ensureCapacityInternal call, so I guess modCount ++ on ensureCapacityInternal inside is pure broken To make it easier, even if some did not modify the array of scenarios can lead to modCount be modified.

3. Delete:

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

    return oldValue;
}

The positions of all the elements after the deletion moved forward one position, noting that the last air out of the memory location to be set to null, otherwise it will be a memory leak .

4, bulk delete

public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}
public boolean retainAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, true);
}

private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;
    int r = 0, w = 0;
    boolean modified = false;
    try {
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];
    } finally {
        // Preserve behavioral compatibility with AbstractCollection,
        // even if c.contains() throws.
        if (r != size) {
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        if (w != size) {
            // clear to let GC do its work
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

removeAll delete all the elements of the set parameters in turn retainAll delete all elements not in the set parameters inside, both of which are achieved by batchRemove.

batchRemove two cursors w, R & lt performing successive element is moved, w for write position, R & lt representative of the reading position, the reading is determined whether an element should be removed, then continue to the next if a read, write or location w . Thus, in the end position is the end of the list of w, w ~ r between the position needs to be set to null.

It is worth noting that the final finishing logical finally put inside, to ensure a certain degree of abnormal security. If an exception occurs, the remaining non-scanned elements (elements in position after r), after the copy to be W; Thus, batchRemove possible to perform half failed, but the state is not ArrayList mess.

Iterator

ArrayList supports two iterators, Iterator and ListIterator, which is an enhanced version of the former, you can move forward, insert elements, returns the element index.

On the interpretation here under Iterator achieve, ListIterator is about the same.

1, Iterator member variables

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor != size;
    }
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }

Iterator implemented as a non-static internal ArrayList, so you can access the member fields ArrayList directly, it only needs to remember the current position (cursor) can be. lastRet point to a next element position operation returns, this is very necessary, because in the iterator element if you want to do something, all the elements for this position.

expectedModCount is a snapshot ArrayListmodCount prevent the iterative process, unexpected changes to the execution list.

2, next operation

@SuppressWarnings("unchecked")
public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}

Very simple, almost nothing interpretable.

3, remove operation

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();
    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

remove action updates expectedModCount, which is a permitted modification operations to be expected.

. 4, ModCount
ModCount AbstractList defined in the base class which, using a modified track list, its value is of no practical significance.

modCount mainly used most, is when unexpected changes occur, fail fast, fail rather than wait until the state appears difficult to track data error. The idea is, if during the execution of an operation, if not expect ArrayList be modified by other operations, you can record it at the beginning of modCount snapshot, in the course of operation, determined by modCount and this snapshot comparison ArrayList of ArrayList It is being modified, and then throw a ConcurrentModificationException.

ConcurrentModificationException looks like multithreading related, but actually there and had nothing to do multithreading, ArrayList is not thread safe, modCount design nor for multi-threaded meaning.

SubList

The cursor subList ArrayList directly locked by a segment of the original ArrayList, thereby preventing copy.
Because SubList to implement all of the interface List, all the source code more, explain realize here get method, the other is similar.

private class SubList extends AbstractList<E> implements RandomAccess {
    private final AbstractList<E> parent;
    private final int parentOffset;
    private final int offset;
    int size;

    SubList(AbstractList<E> parent,
            int offset, int fromIndex, int toIndex) {
        this.parent = parent;
        this.parentOffset = fromIndex;
        this.offset = offset + fromIndex;
        this.size = toIndex - fromIndex;
        this.modCount = ArrayList.this.modCount;
    }
    
    public E get(int index) {
        rangeCheck(index);
        checkForComodification();
        return ArrayList.this.elementData(offset + index);
    }
    public E set(int index, E e) {
        rangeCheck(index);
        checkForComodification();
        E oldValue = ArrayList.this.elementData(offset + index);
        ArrayList.this.elementData[offset + index] = e;
        return oldValue;
    }
}

1, first, a constructor can be seen, subList is actually operated by the index data of the original ArrayList, but adding an offset (offset) and length limitations (size).

2, get method checks modCount, explained in SubList life cycle, expect parent ArrayList will not be modified. This also shows that SubList only suitable for temporary variables , not suitable for long-term survival, unless the original ArrayList is unchanged.

AbstractList

ArrayList is AbstractList base class, it implements a version Iterator, ListIterator, SubList of. But AbstractList not know the implementation details stored, it can only be based on a common list of interfaces to achieve, so efficiency will definitely poor.

For example, next method of Iterator AbstractList is implemented as follows:

public E next() {
    checkForComodification();
    try {
        int i = cursor;
        E next = get(i);
        lastRet = i;
        cursor = i + 1;
        return next;
    } catch (IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
    }
}

Gets the element using a method list.get: E next = get(i).

Guess you like

Origin www.cnblogs.com/longhuihu/p/11123095.html