ArrayList is a collection. ArrayList is a collection of objects that you can add, delete, change, and iterate through several ways of playing. Let’s see how an ArrayList can add, delete, modify, and iterate.
Before we look at these operations, we need to understand what a few parameters of an ArrayList mean
/ / array
transient Object[] elementData;
// Array default size
private static final int DEFAULT_CAPACITY = 10;
// Set size
private int size;
// The maximum space that the Jvm can allocate to an array
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// Number of versions
protected transient int modCount = 0;
Copy the code
- elementsData
This is an array of type Object that is maintained internally by an ArrayList. Second, the type is Object, so we can only pass in objects, we can’t pass in things like ints, because if you pass in ints, that’s all going to be type wrapped.
- DEFAULT_CAPACITY
This refers to the default size of the elementsData array
- size
I’m talking about the size of the set, not the size of the array, so you can’t confuse that
- MAX_ARRAY_SIZE
This is the maximum amount of space that the Jvm can allocate to an array, and if it exceeds that, an error will be reported
- modCount
< AbstractList > > < AbstractList > < AbstractList > > < AbstractList > > < AbstractList > > < AbstractList > > < AbstractList > > < AbstractList >
- structure
The first is to create an ArrayList, which provides three constructs: To determine what size is expected, either set the array to a fixed length, an array of length 0, or an error (this is an int, so Integer.MAX_VALUE is not greater than Integer.)
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); }}Copy the code
Create an array object with length 0
public ArrayList(a) {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
Copy the code
The third: According to the Collection Object passed in, toArray directly creates an array Object. Then, some judgments are made to determine whether the Collection passed in has a value. If there is a value, the judgment is made to determine whether it is of Object type. If the Collection is passed with no value, an empty array of objects is created
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if((size = elementData.length) ! =0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if(elementData.getClass() ! = Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); }else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA; }}Copy the code
- add
ArrayList adding elements is one of the highlights, it’s an interesting scaling mechanism, so let’s look at these two ways to add a single element, and enrecapacityInternal is our scaling, so let me show you that for a second, the first method, It’s just the size of the array, and then it’s going to be ++, size++ means first, then ++, so our subscript starts at 0. The second method, adding to a location, does a judgment, which is easy to understand, then expands, then copies the array, moves it back one bit, and then assigns the current index position, roughly as follows
/ / the first
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true; } the secondpublic void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
Copy the code
EnsureCapacityInternal is a method that does scaling, so when we first add data, elementData is empty, so our array is 10; The value minCapacity represents the size we expect
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
Copy the code
Version count +1, and then determine if the expected value is greater than the current array length, if it is, then it really needs to be expanded
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
Copy the code
Here’s the actual code for the expansion, so I’ll comment it out directly in the code; Expand our current array size 1.5 times (can remember a very classic point here, the array is smaller, the smaller the expansion after the results, the greater the array, the greater the expansion after the results, you, you are fine, isn’t it save resources, to the space when the data is little less, does not occupy a space, data when give you enlarge more at a time, also can avoid multiple trigger expansion)
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//oldCapacity >> 1 oldCapacity*0.5
int newCapacity = oldCapacity + (oldCapacity >> 1);
// Whether the current expanded length is greater than our expected value, if not, then the expected value is used as the new length value
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// Determine if the maximum amount of space allocated by the JVM is exceeded, and if so, use integer.max_value as the new length value
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// Expand capacity by replication
elementData = Arrays.copyOf(elementData, newCapacity);
}
Copy the code
- remove
Change the version of the array by +1 if the array length is affected.
public E remove(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
modCount++;
// Get the element I want to delete and return it later
E oldValue = (E) elementData[index];
// Calculate how many elements I need to move forward from index
int numMoved = size - index - 1;
if (numMoved > 0)
// I want to move the index position +1 and all subsequent elements forward one bit
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// Empty the element I want to remove to help the GC with the collection, and then reduce the size of the collection by 1
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
Copy the code
We can add null to our collection, so we’ll delete the first null. If we don’t delete null, we’ll go to Equals and delete the first one (so, The ArrayList will only remove the objects one by one each time it executes them, so don’t make the mistake of deleting all the matching objects in the list.
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true; }}else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true; }}return false;
}
Copy the code
private void fastRemove(int index) {
modCount++;
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
}
Copy the code
- get
- set
These two are either going to get the elements in the array or modify the elements in the array
public E get(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
return (E) elementData[index];
}
Copy the code
public E set(int index, E element) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
Copy the code
- traverse
Traverse we can use fori, foreach iterator, here since the ArrayList is the source, said the ArrayList provide iterator, the adapter is to implement the iterator interface, then the ArrayList is no exception He has a few core parameters
protected int limit = ArrayList.this.size;// Size of the current collection
// Position of the next element
int cursor; // index of next element to return
// The position of the element in the last iteration, if the last iteration was deleted, then our lastRet is -1
int lastRet = -1; // index of last element returned; -1 if no such
// The expected version number during the iteration, initialized as the number of changes to our version
int expectedModCount = modCount;
Copy the code
There are also several core methods to determine if the current element position is larger than the set size, and each time next is cursor +1
public boolean hasNext(a) {
return cursor < limit;
}
Copy the code
We compare whether the version number we expect is the same as the version number of the actual array. If not, we throw an exception
@SuppressWarnings("unchecked")
public E next(a) {
if(modCount ! = expectedModCount)throw new ConcurrentModificationException();
int i = cursor;
if (i >= limit)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
// Each time next, set cursor+1
cursor = i + 1;
// Return our current next value and assign it to lastRet
return (E) elementData[lastRet = i];
}
Copy the code
Delete when compared with the above the version number, and our version number in each time you add, delete, change, so we in the traversal, is cannot do increase the delete operation to the list, or it will throw an exception, because will certainly is not the same as the version number, and with the deletion of the iterator is different, After performing the remove method, he will copy the latest version of our current array to the expectedModCount to ensure the consistency of the two versions. The above need to be associated to a point, no matter how you iterate, you can not directly add and delete the set while iterate. For example, foreach’s underlying traversal calls next and does not change the version number, so if you go directly to list.remove, the version number will be different
public void remove(a) {
if (lastRet < 0)
throw new IllegalStateException();
if(modCount ! = expectedModCount)throw new ConcurrentModificationException();
try {
// Perform the delete operation
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
// Synchronize the current version number
expectedModCount = modCount;
// The current length is -1
limit--;
} catch (IndexOutOfBoundsException ex) {
throw newConcurrentModificationException(); }}Copy the code
- conclusion
ArrayList is thread-unsafe. We see no locking operations there. If you want to lock, you can use the Collections synchronizedList. The System. Arraycopy is used to copy the array each time it is added or deleted. The search and update are directly assigned to or obtained from the array, so it is inefficient to add or delete the array each time it is copied. Including its data expansion is also performed copy operation; ArrayList’s expansion mechanism is pretty cool, can be learned; The upper and lower limits of an ArrayList, positioned at 0-INTEger. MAX_VALUE, will not cause developers to endlessly add