Statement: respect other people’s labor achievements, reproduced please attach original link!

1. ArrayList inheritance



ArrayListAlso known as dynamic array, the underlying List is implemented based on array. The difference with array is that it has dynamic expansion capability. As can be seen from the diagram of inheritance systemArrayList:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess.Cloneable.java.io.Serializable {... }Copy the code
  • Implement List, RandomAccess, Cloneable, Java. IO.Serializable interface
  • List, with basic add, delete, traversal and other operations
  • Achieve RandomAccess, have the ability to access randomly
  • Implemented Cloneable, can be cloned (shallow copy)list.clone()
  • Serializable is implemented and can be serialized

2. ArrayList implements Cloneable, RandomAccess, Serializable interfaces

Cloneable interface can be shallow copy

 /** * Returns a shallow copy of this <tt>ArrayList</tt> instance. (The * elements themselves are not copied.) ArrayList </ tt> copy of an instance. (The element itself is not copied.) * *@return a clone of this <tt>ArrayList</tt> instance
  */
 public Object clone(a) {
     try{ ArrayList<? > v = (ArrayList<? >)super.clone();
         / / copy
         v.elementData = Arrays.copyOf(elementData, size);
         v.modCount = 0;
         return v;
     } catch (CloneNotSupportedException e) {
         // this shouldn't happen, since we are Cloneable
         throw newInternalError(e); }}Copy the code

In The Java language, objects can be deep-copied or shallow-copied when implementing the Cloneable interface. Here are the differences between deep-copied and shallow-copied objects:

First create a User class:

/ * * *@Auther: csp1999
 * @Date: 2020/10/28/15:51
 * @Description: * /
public class User {
    private String name;/ / user name
    public String getName(a) {returnname; }public void setName(String name) {this.name = name; }
    public User(String name) { this.name = name; }public User(a) {}
    @Override
    public String toString(a) {
        return "User{" +
                "name='" + name + '\' ' +
                '} '; }}Copy the code

Here’s an example of a shallow copy:

@Test
public void test02(a) {
    /** * simple demonstration of ArrayList shallow copy: */
    ArrayList list = new ArrayList();
    // Instantiate the User object
    User user = new User("csp");
    list.add("abc");// list adds simple character types
    list.add(user);// list Adds the object type
    System.out.println("------------ Original ArrayList collection ------------");
    System.out.println("list:"+list);
    / / shallow copy
	ArrayList clone = (ArrayList) list.clone();
	System.out.println("------------ New ArrayList collection after shallow copy ------------");
	System.out.println("Are the addresses the same:" + (list == clone));
	System.out.println("------------ before modifying the User attribute in list ------------");
	System.out.println("clone:"+clone);
	System.out.println("------------ after modifying the User attribute in the list ------------");
	User u = (User) list.get(1);
	u.setName("csp1999");
	System.out.println("clone:"+clone);
	System.out.println(Clone user object address is the same as the original list user object address:+(list.get(1)==clone.get(1)));
}
Copy the code

Output result:

-- -- -- -- -- -- -- -- -- -- -- -- original ArrayList collection -- -- -- -- -- -- -- -- -- -- -- -- the list: [ABC, User {name = 'CSP'}] -- -- -- -- -- -- -- -- -- -- -- -- shallow copy after new ArrayList collection -- -- -- -- -- -- -- -- -- -- -- -- the two address is the same: False -- -- -- -- -- -- -- -- -- -- -- -- not to modify the list of User attributes before -- -- -- -- -- -- -- -- -- -- -- -- clone: [ABC, User {name = 'CSP'}] -- -- -- -- -- -- -- -- -- -- -- -- after modify User attributes of the list -- -- -- -- -- -- -- -- -- -- -- -- clone: [ABC, Clone User{name='csp1999'}] Check whether the address of the User object is the same as that of the original list: trueCopy the code

It can be concluded from the results that:

  • When the original list collection is cloned, a new one is actually createdArrayListObject with different address references
  • When the user object is obtained from the list set and its attributes are modified, the user attribute in the Clone set is also changed accordingly. This indicates that when Clone copies the list set, it only copies the address reference of the complex object type data elements without creating new objects
  • Therefore, the implementationlist.get(1)==clone.get(1))At the time of the code, both stores the same User object with the same address and data

In the case of deep copy, when the list is cloned to create new cloned objects, the complex object types stored in the list are also created. The complex object type data obtains new address references, instead of copying only the address references of the complex object types as in the shallow copy. \

ArrayList implements the RandomAccess interface, so random lists can be accessed more efficiently than sequential lists. Let’s look at an example:

@Test
public void test03(a) {
    ArrayList arrayList = new ArrayList();
    for (int i=0; i<=99999; i++){// Add 100000 data to the collection
        arrayList.add(i);
    }
    // Test random access efficiency:
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < arrayList.size(); i++) {// Random access
        // Access each element from the collection
        arrayList.get(i);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Time to perform random access:"+(endTime-startTime));
    // Test the efficiency of sequential access:
    startTime = System.currentTimeMillis();
    Iterator it = arrayList.iterator();// Sequential access, enhanced for can also be used
    while (it.hasNext()){
        // Access each element from the collection
        it.next();
    }
    endTime = System.currentTimeMillis();
    System.out.println("Time to perform sequential access:"+(endTime-startTime));
}
Copy the code

View the output:

Time used for random access: 1 Time used for sequential access: 3Copy the code

You can see that an ArrayList implementing the RandomAccess interface is more efficient for RandomAccess than for sequential access. For comparison, let’s take a look at a collection of LinkedLists that do not implement the RandomAccess interface to test the efficiency of randomly and sequentially accessed lists:

@Test
public void test04(a) {
    LinkedList linkedList = new LinkedList();
    for (int i=0; i<=99999; i++){// Add 100000 data to the collection
        linkedList.add(i);
    }
    // Test random access efficiency:
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < linkedList.size(); i++) {// Random access
        // Access each element from the collection
        linkedList.get(i);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Time to perform random access:"+(endTime-startTime));
    // Test the efficiency of sequential access:
    startTime = System.currentTimeMillis();
    Iterator it = linkedList.iterator();// Sequential access, enhanced for can also be used
    while (it.hasNext()){
        // Access each element from the collection
        it.next();
    }
    endTime = System.currentTimeMillis();
    System.out.println("Time to perform sequential access:"+(endTime-startTime));
}
Copy the code

View the output:

Time used for random access: 4601 Time used for sequential access: 2Copy the code

It can be concluded from the results that the efficiency of testing RandomAccess is much lower than sequential access without implementing the collection of LinkedList interface.

\

3. The ArrayList properties

/** * Default initial capacity. */
private static final int DEFAULT_CAPACITY = 10;
/** * Shared empty array instance used for empty instances
private static final Object[] EMPTY_ELEMENTDATA = {};
/** * Shared empty array instance used for default sized empty instances. * We distinguish this from EMPTY_ELEMENTDATA [public args] {public args [public args] {public args [public args] {public args [public args] {public args [public args] {public args [public args] {public args [public args] {public args [public args] {public args [public args] {public args [public args]}
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. * The array container in the collection that actually stores the data elements */
transient Object[] elementData; // non-private to simplify nested class access
/** * The size of The ArrayList (The number of elements it contains). * The number of elements in The set *@serial* /
private int size;
Copy the code
  • DEFAULT_CAPACITY: The default capacity of the collection. The default capacity is 10new ArrayList()The default capacity for creating an instance of the List collection is 10.
  • EMPTY_ELEMENTDATA: Empty array, passnew ArrayList(0)The empty array is used to create an instance of the List collection.
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA: Default capacity empty array, this is passednew ArrayList()Parameterless constructorThe collection is created with this empty array, which differs from EMPTY_ELEMENTDATA in that using this empty array when adding the first element will initialize DEFAULT_CAPACITY(10) elements.
  • ElementData: An array that stores data elements, using the transient modifier. This field is not serialized.
  • Size: Number of stored data elements. The length of the elementData array is not the number of stored data elements.

4. ArrayList constructor

ArrayList(int initialCapacity) has a parameter constructor

/** * Constructs an empty list with the specified initial capacity. * Constructs an empty list with the specified initial capacity@paramInitialCapacity The initial capacity of the list Initial capacity *@throwsIllegalArgumentException if the specified Initial capacity is negative * * Use the EMPTY_ELEMENTDATA empty array if it is 0, and throw an exception if it is less than 0. * /
// ArrayList(int initialCapacity) constructor
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Unreasonable capacity of first knowledge:"+ initialCapacity); }}Copy the code

ArrayList() empty argument constructor

 Constructs an empty list with an initial capacity of ten ** Constructs an empty list with an initial capacity of ten ** Constructs an empty list with an initial capacity of ten Initialize to DEFAULTCAPACITY_EMPTY_ELEMENTDATA, and the * will be expanded to the default size of 10 when the first element is added. * /
 public ArrayList(a) {
     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
 }
Copy the code

ArrayList(Collection C) has parameter constructors

/** * Constructs a list containing the elements of the specified * collection, In the order they are returned by the collection's * iterator@param c the collection whose elements are to be placed into this list
 * @throws NullPointerException if the specified collection is null
 */
public ArrayList(Collection<? extends E> c) {
    // Convert the collection arguments in the constructor to arrays
    elementData = c.toArray();
    if((size = elementData.length) ! =0) {
        // Check whether c. toarray () returns an Object[]. If not, copy it back to Object[]. Class
        if(elementData.getClass() ! = Object[].class)// Create and copy an array
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // If c is an empty collection, initialize the empty array EMPTY_ELEMENTDATA
        this.elementData = EMPTY_ELEMENTDATA; }}Copy the code

Question: Why check to see if c. toarray () returns Object[] and copy it to Object[]. Class? First of all, we all know that Object is a super superclass in Java. All classes inherit from Object indirectly or directly. Let’s look at an example:

/ * * *@Auther: csp1999
 * @Date: 2020/10/27 / "*@Description: * /
public class ArrayListTest {
    class Father {}
    class Son extends Father {}
    class MyList extends ArrayList<String> {
        /** * subclass overrides the method of the parent class. The return value can be different, but only the array type can be used. * Object does not work
        @Override
        public String[] toArray() {
            // Write to death for example
            return new String[]{"a"."b"."c"}; }}@Test
    public void test01(a) {
        Father[] fathers = new Son[]{};
        / / print the results as follows: the class [Lcom. Haust. Test. ArrayListTest $Son;
        System.out.println(fathers.getClass());

        List<String> strList = new MyList();
        // Prints the result: class [ljava.lang.String;System.out.println(strList.toArray().getClass()); }}Copy the code

5. Operations related to ArrayList

Add (E E) adds elements to the collection

Adding elements to the end takes O(1) on average:

 /** * 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) {
     // For each element added, minCapacity is +1 and check whether it needs to be expanded
     ensureCapacityInternal(size + 1);  // Increments modCount!!
     // Insert the element to the last digit
     elementData[size++] = e;
     return true;
 }
// Calculate the minimum capacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // Return the larger part of DEFAULT_CAPACITY and minCapacity
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
// Check whether the capacity needs to be expanded
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;// The number of times the array structure is modified +1
    // Overflow-conscious code stores elements with a size smaller than the required minimum
    if (minCapacity - elementData.length > 0)
        / / capacity
        grow(minCapacity);
}
/** * Increases the capacity to ensure that it can hold at lea * number of elements specified by the minimum Capacity ARg * Increments the capacity to ensure that it can hold at least the number of elements specified by the minimum capacity parameter *@param minCapacity the desired minimum capacity
 */
private void grow(int minCapacity) {
    // The original capacity
    int oldCapacity = elementData.length;
    // The new capacity is 1.5 times the old capacity
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // If the new capacity is found to be smaller than the required capacity, the required capacity prevails
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // If the new capacity exceeds the maximum capacity, use the maximum capacity
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // Copy a new array with the new capacity
    elementData = Arrays.copyOf(elementData, newCapacity);
}
// Use the maximum capacity
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
}
Copy the code

Execution process:

  • Check whether capacity expansion is required.
  • If elementData is equal to DEFAULTCAPACITY_EMPTY_ELEMENTDATA, initialize the capacity to DEFAULT_CAPACITY.
  • The new capacity is 1.5 times of the oldCapacity (oldCapacity + (oldCapacity >> 1)). If the capacity is smaller than the required capacity, the required capacity prevails.
  • Create a new size array and copy the old array into the new array.

Add (int index, E Element) Adds an element to the specified position

Add elements to the specified location in an average time of O(n) :

/** * Inserts the specified element at the specified position in this * list. Shifts the element currently at that Position (if any) and * any subsequent elements to the right (adds one to their indices). * The average time complexity of adding an element to a specified location is O(n). * *@paramIndex Specifies the index * of the element to be inserted@paramElement Specifies the element to be inserted@throws IndexOutOfBoundsException {@inheritDoc} * /
public void add(int index, E element) {
    // Check for overbounds
    rangeCheckForAdd(index);
    // Check whether the capacity needs to be expanded
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // Move inex and the element after it one bit back to leave the index position empty
    // ** performs the size- index operation **
    System.arraycopy(elementData, index, elementData, index + 1,
            size - index);
    // Insert the element at the index position
    elementData[index] = element;
    // The number of elements increases by 1
    size++;
}
/** * A version of rangeCheck used by add and addAll
// Check for overbounds
private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
Copy the code

Execution process:

  • Check if the index is out of bounds;
  • Check whether capacity expansion is required.
  • Move all elements one bit back after insertion;
  • Place the inserted element at the insert index position;
  • Number of elements increased by 1;

AddAll (Collection C) adds all elements of all Collection parameters

Find the union of two sets:

/** * Appends all of the elements in the specified collection to the end of * this list, in the order that they are returned by the * specified collection's Iterator. The behavior of this operation is * undefined if the specified collection is modified while the operation * is in progress. (This implies that the behavior of this call is * undefined if the specified collection is this list, And this * list is nonempty.) * Adds all elements from collection C to the current ArrayList * *@param c collection containing elements to be added to this list
 * @return <tt>true</tt> if this list changed as a result of the call
 * @throws NullPointerException if the specified collection is null
 */
public boolean addAll(Collection<? extends E> c) {
    // Convert collection C to an array
    Object[] a = c.toArray();
    int numNew = a.length;
    // Check whether the capacity needs to be expanded
    ensureCapacityInternal(size + numNew);  // Increments modCount
    // Copy all elements in c to the end of the array
    System.arraycopy(a, 0, elementData, size, numNew);
    // The size of the elements in the collection increases the size of c
    size += numNew;
    // Return true if c is not empty, false otherwise
    returnnumNew ! =0;
}
Copy the code

Execution process:

  • Copy elements from c into array A;
  • Check whether capacity expansion is required.
  • Copy elements from array A to the end of elementData;

Get (int index) Gets the element at the specified index position

Gets the element at the specified index position in O(1) time.

/**
 * 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) {
    // Check for overbounds
    rangeCheck(index);
    // Return the element in the array index position
    return elementData(index);
}
/** * Checks if the given index is in range. If not, throws an appropriate * runtime exception. This method does *not* check if the index is * negative: It is always used immediately prior to an array access, * which throws an ArrayIndexOutOfBoundsException if index is negative. * check whether the given index range * / number in a set effective elements
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
@SuppressWarnings("unchecked")
E elementData(int index) {
    return (E) elementData[index];
}
Copy the code

Execution process:

  • Check index of crossing the line, here only to check whether the upper bound, if the upper throw IndexOutOfBoundsException abnormalities, if the lower bound is an ArrayIndexOutOfBoundsException anomalies.
  • Returns the element at the index position;

Remove (int index) Removes the element at the specified index position

Removes the element at the specified index position in O(n) time.

/** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (SubTRACTS one from their * indices). * Delete the element at the specified index position. The time complexity is O(n). * *@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) {
    // Check for overbounds
    rangeCheck(index);
    // The number of changes to the underlying array structure of the collection +1
    modCount++;
    // Get the element at index position
    E oldValue = elementData(index);
    int numMoved = size - index - 1;
    // If index is not the last digit, move the element after index one digit forward
    if (numMoved > 0)
    	// ** perform the size- index-1 operation **
        System.arraycopy(elementData, index + 1, elementData, index,
                numMoved);
    // Remove the last element to help the GC
    elementData[--size] = null; // clear to let GC do its work
    // Return the old value
    return oldValue;
}
Copy the code

Execution process:

  • Check if the index is out of bounds;
  • Gets the element at the specified index position;
  • If not the last bit is deleted, the other elements move forward one bit;
  • Set the last position to null for GC collection;
  • Returns the deleted element.

Note: From the source code, ArrayList deletes elements without shrinking. \

Remove (Object o) Removes the element with the specified element value

Removes the element with the specified element value in O(n) time.

/** * Removes the first occurrence of the specified element from this list, * if it is present. If the list does not contain the element, it is * unchanged. More formally, removes the element with the lowest index * <tt>i</tt> such that * <tt>(o==null&nbsp; ? &nbsp; get(i)==null&nbsp; :&nbsp; o.equals(get(i)))</tt> * (if such an element exists). Returns <tt>true</tt> if this list * contained the specified Element (or equivalently, if this list * changed as a result of the call). * Remove the element with the specified element value in O(n) time. * *@paramO element to be removed from this list, if present * The element to be removed from this list (if it exists) *@return <tt>true</tt> if this list contained the specified element
 */
public boolean remove(Object o) {
    if (o == null) {
        // Walk through the array, find the first occurrence of the element, and quickly delete it
        for (int index = 0; index < size; index++)
            // If the element to be deleted is NULL, null is used for comparison, using ==
            if (elementData[index] == null) {
                fastRemove(index);
                return true; }}else {
        // Walk through the array, find the first occurrence of the element, and quickly delete it
        for (int index = 0; index < size; index++)
            // If the elements to be deleted are not null, compare them using equals()
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true; }}return false;
}
/* * Private remove method that skips bounds checking and does not * return the value removed. And does not return the deleted value. * /
private void fastRemove(int index) {
    // There is one less out-of-bounds check
    modCount++;
    // If index is not the last digit, move the element after index one digit forward
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index + 1, elementData, index,
                numMoved);
    // Remove the last element to help the GC
    elementData[--size] = null; // clear to let GC do its work
}
Copy the code

Execution process:

  • Finds the first element equal to the specified element value;
  • Quick delete; FastRemove (int index) has fewer operations than remove(int index) to check index overbounds, so the JDK optimizes performance to the extreme.

RetainAll (Collection C) finds the intersection of two sets

* Retains only the elements in this list that are contained in the * specified collection. In other words, removes from this list all * of its elements that are not contained in the specified collection. * *@param c collection containing elements to be retained in this list
 * @return {@code true} if this list changed as a result of the call
 * @throws ClassCastException   if the class of an element of this list
 *                              is incompatible with the specified collection
 *                              (<a href="Collection.html#optional-restrictions">opti
 * @throwsNullPointerException if this list contains a null element and the * specified collection does not permit null elements *  (<a href="Collection.html#optional-restrictions">opti * or if the specified collection is null *@see Collection#contains(Object)
 */
public boolean retainAll(Collection
        c) {
    // Set C cannot be null
    Objects.requireNonNull(c);
    // Invoke the bulk delete method, where complement is passed true to delete elements not included in c
    return batchRemove(c, true);
}
/** * Delete elements in batch. * complement true deletes elements not included in c. * Complement false deletes elements contained in C@param c
 * @param complement
 * @return* /
private boolean batchRemove(Collection<? > c,boolean complement) {
    final Object[] elementData = this.elementData;
    /** * use both Pointers to iterate over the array ** increments the read pointer each time, and increments the write pointer each time it is added to an element */
    int r = 0, w = 0;
    boolean modified = false;
    try {
        // Traverse the array. If c contains the element, put it in the same position as the pointer.
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];
    } finally {
        // Normally r is equal to size unless c. containes () throws an exception
        if(r ! = size) {// If c. taines () throws an exception, copy the unread elements to the write pointer
            System.arraycopy(elementData, r,
                    elementData, w,
                    size - r);
            w += size - r;
        }
        if(w ! = size) {// Set the element after the write pointer to null to help GC
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            // The new size is equal to the position of the write pointer.
            size = w;
            modified = true; }}// Returns true with modifications
    return modified;
}
Copy the code

Execution process:

  • Iterate over the elementData array;
  • If the element is in C, add the element to the W position of the elementData array and move the W position back one bit;
  • After traversal, everything before W is common to both, and everything after W is not common to both;
  • Set elements after w to null for GC collection;

RemoveAll (Collection C) computes the unidirectional difference of two sets

Find the unidirectional difference set of two sets, reserving only the elements in the current set that are not in C, and not the elements in C that are not in the current group.

/** * Removes from this list all of its elements that are contained in the * specified collection. * Removes all elements contained in the specified collection from this collection. * *@param c collection containing elements to be removed from this list
 * @return {@code true} if this list changed as a result of the call
 * @throws ClassCastException   if the class of an element of this list
 *                              is incompatible with the specified collection
 *                              (<a href="Collection.html#optional-restrictions">optional</a>)
 * @throwsNullPointerException if this list contains a null element and the * specified collection does not permit null elements *  (<a href="Collection.html#optional-restrictions">optional</a>), * or if the specified collection is null *@see Collection#contains(Object)
 */
public boolean removeAll(Collection
        c) {
    // Set c cannot be empty
    Objects.requireNonNull(c);
    // Call the batch delete method, here passing false to complement the element contained in c
    return batchRemove(c, false);
}
Copy the code

This is similar to the retainAll(Collection C) method, except that elements that are not in C are retained. \

Expand your knowledge

ArrayList toString()

ToStrin () = toStrin(); toStrin() = toStrin(); Did not directly in the ArrayList source toString () method, we need to the parent class AbstractList superclass AbstractCollection looking for:

/**
 * Returns a string representation of this collection.  The string
 * representation consists of a list of the collection's elements in the
 * order they are returned by its iterator, enclosed in square brackets
 * (<tt>"[]"</tt>).  Adjacent elements are separated by the characters
 * <tt>", "</tt> (comma and space).  Elements are converted to strings as
 * by {@link String#valueOf(Object)}.
 *
 * @return a string representation of this collection
 */
public String toString(a) {
    Iterator<E> it = iterator();// Get the iterator
    if (! it.hasNext())// Return if null
        return "[]";
    // StringBuilder concatenates strings
    StringBuilder sb = new StringBuilder();
    sb.append('[');
    for (;;) {// Infinite loop == while(true)
        E e = it.next();// The iterator next method takes the element and moves the cursor back
        sb.append(e == this ? "(this Collection)" : e);// Make a triple judgment
        if (! it.hasNext())
            return sb.append('] ').toString();// If there are no elements, concatenate the close parenthesis
        sb.append(', ').append(' ');// There are elements}}Copy the code
<br> ### 7. ** * (1) ArrayList uses an array to store elements. If the size of the array is insufficient, the size of the ArrayList is not reduced. (2) ArrayList supports random access. Accessing elements through indexes is extremely fast and the time complexity is O(1); (3) ArrayList adds elements to tail very fast, with an average time complexity of O(1); (4) ArrayList is slow to add elements to the middle, because the average time complexity of moving elements is O(n); (5) ArrayList removes elements from the tail very quickly, and the time complexity is O(1); (6) ArrayList is slow to remove elements from the middle, because the average time complexity of moving elements is O(n); (7) ArrayList supports union, call addAll(Collection C) method can be; (8) ArrayList support intersection, call retainAll(Collection C) method; (7) ArrayList support one-way difference set, call removeAll(Collection C) method; <br> > Q: **elementData is set to TRANSIENT, so how does ArrayList serialize elements **? ```java /** * Save the state of the <tt>ArrayList</tt> instance to a stream (that * is, Serialize it). * Saves the state of < TT > ArrayList </ TT > instances to the stream (i.e., Emitted by The world's worst policymakers * * @serialdata The length of The array backing The <tt>ArrayList</tt> * instance is emitted (int), followed by all of its elements * (each an <tt>Object</tt>) in the proper order. */ private void WriteObject (Java. IO. ObjectOutputStream s) throws Java IO. IOException {/ / prevent there are changes during serialization int expectedModCount = modCount; // write a non-transient non-static property (size property will be written). // write the number of elements s.riteint (size); // write elements for (int I = 0; i < size; i++) { s.writeObject(elementData[i]); } // If there are changes, throw an exception if (modCount! = expectedModCount) { throw new ConcurrentModificationException(); } } /** * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is, * deserialize it). * from the flow of refactoring < tt > ArrayList < / tt > instance (i.e., the deserialization) * / private void readObject (Java. IO. ObjectInputStream s) throws Java.io.IOException, ClassNotFoundException {// Declare an empty array elementData = EMPTY_ELEMENTDATA; // Read a non-transient non-static property (size will be read). // read s.readint (); // read s.readint (); // Ignored if (size > 0) {int capacity = calculateCapacity(elementData, size); SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity); // Check whether ensureCapacityInternal(size) needs to be expanded; Object[] a = elementData; For (int I = 0; i < size; i++) { a[i] = s.readObject(); }}}Copy the code

The writeObject() method is used to write size to the stream, and then write elements to the stream one by one.

In general, the Serializable interface can be automatically serialized. WriteObject () and readObject() must be declared private in order to control the way they are serialized. In Java. IO. ObjectStreamClass# getPrivateMethod () method through reflection get writeObject () this method.

The s.d_FaultwriteObject () method is first called in ArrayList’s writeObject() method, which writes a non-static, non-transient property, which in ArrayList is the size property. Likewise, the size property is resolved by calling the s.dexfaultreadObject () method first in the readObject() method.

ElementData is defined as transient and has the advantage of serializing real elements by size rather than by array length, reducing space footprint.

7.ArrayList

7.1 How can I Expand ArrayList?

Expand the capacity by 10 for the first time and 1.5 times the original capacity for each subsequent expansion. Move 1 bit to the right through the bit operation.Copy the code

7.2 How can I Solve the Problem that Add Performance Deteriorates Due to Frequent ArrayList Capacity Expansion?

Directly on the code:

@Test
public void test06(a) {
    // ArrayList Performance deteriorates rapidly due to frequent capacity expansion. What can I do?
    // Examples are as follows:
    ArrayList arrayList = new ArrayList();
    long startTime = System.currentTimeMillis();
    // Add 100W pieces of data to the collection
    for (int i = 0; i < 1000000; i++) {
        arrayList.add(i);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Before optimization, add 1 million data when:" + (endTime - startTime));
    System.out.println("Below --------------- is the solution: ------------------");
    ArrayList arrayList2 = new ArrayList(1000000);
    startTime = System.currentTimeMillis();
    // Add 100W pieces of data to the collection
    for (int i = 0; i < 1000000; i++) {
        arrayList2.add(i);
    }
    endTime = System.currentTimeMillis();
    System.out.println("After optimization, add 1 million data when:" + (endTime - startTime));
}
Copy the code

Output result:

Before optimization, add 100000 data available: 58 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- here's the solution: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- after the optimization, add 100000 data available: 13Copy the code

As you can see, if you define the initial capacity of the ArrayList collection in advance when a large amount of data needs to be added to the collection, you don’t have to spend a lot of time on automatic expansion

7.3 Is ArrayList necessarily slower to insert or delete elements than LinkedList?

From the underlying data structure of the two:

  • ArrayList is a data structure that implements dynamic arrays
  • LinkedList is based on a LinkedList data structure.

Efficiency comparison:

  • Header insert: Inserting data into the header of the LinkedList is fast because you only need to change the prev and next values of the nodes before and after the element is inserted. Inserting data into the head of an ArrayList is slow because array copying takes a lot of time to shift.
  • Intermediate insertion: intermediate insertion of LinkedList is slow because traversing the LinkedList pointer (binary lookup) takes time; The middle of an ArrayList is faster to insert data, because it is faster to locate inserted elements, and there are fewer elements to shift.
  • Tail insert: data is slow to insert at the end of LinkedList because traversing the LinkedList pointer (binary lookup) takes time; The data inserted at the end of ArrayList is fast. In order to locate the inserted element quickly, the amount of data shifted after insertion is small.

Conclusion:

  • Inserting elements into a collection is faster than inserting a LinkedList in the header. Middle and tail inserts, ArrayList faster;
  • Deleting elements from a collection is similar; deleting the header is faster; Middle and tail delete, ArrayList faster;

Therefore, for a collection with a small amount of data, it is recommended to use LinkedList. For large sets of data, ArrayList can be used, which is not only fast to query, but also relatively efficient to insert and delete.

7.4 Is ArrayList thread safe?

Create a new thread task class:

/ * * *@Auther: csp1999
 * @Date: 2020/10/29 / * instructed@Description: Thread task class */
public class CollectionTask implements Runnable {
    // Share the collection
    private List<String> list;
    public CollectionTask(List<String> list){
        this.list = list;
    }
    @Override
    public void run(a) {
        try {
            Thread.sleep(50);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        // Add the current thread name to the collectionlist.add(Thread.currentThread().getName()); }}Copy the code

Next test:

/ * * *@Auther: csp1999
 * @Date: 2020/10/29 / * same@Description: ArrayList thread safety test class */
public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        // Create a collection
        List<String> list = new ArrayList<>();
        // Create a thread task
        CollectionTask collectionTask = new CollectionTask(list);
        // Start 50 threads
        for (int i = 0; i < 50; i++) {
            new Thread(collectionTask).start();
        }
        // Ensure that the child thread completes execution
        Thread.sleep(3000);
        /** * If the ArrayList is thread-safe, then the collection can be traversed to get 50 pieces of data. If the ArrayList is thread-safe, then the collection can be printed to 50 pieces of data. * /
        // iterate over the collection
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
        System.out.println("Set length:"+list.size()); }}Copy the code

Test results:

null null null Thread-0 Thread-4 Thread-6 Thread-2 Thread-17 Thread-18 Thread-15 Thread-16 Thread-10 Thread-13 Thread-12  Thread-9 Thread-11 Thread-27 Thread-28 Thread-25 Thread-29 Thread-24 Thread-21 Thread-26 Thread-22 Thread-23 Thread-20 Thread-40 Thread-37 Thread-39 Thread-38 Thread-41 Thread-35 Thread-34 null Thread-31 Thread-33 Thread-32 Thread-49 Thread - 46 Thread - 47 48 Thread Thread - 42-43 Thread - the Thread - 45 Thread - 44 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- set length: 45 Process finished with exit code 0Copy the code

Therefore, ArrayList is not a thread-safe collection! If you need to be thread-safe, we recommend using Vector collections, which are thread-safe but less efficient than arrayLists. What makes Vector thread-safe, compared to ArrayList, is its method of adding elements to the collection:

// The Vector add method uses the synchronized keyword
public synchronized void addElement(E obj) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = obj;
}
Copy the code

In addition to Vector collections, we can also use the following methods

List<String> list = new ArrayList<>();
List<String> synchronizedList = Collections.synchronizedList(list);
Copy the code

The resulting synchronizedList is also thread-safe!

Note: When can I not lock an ArrayList synchronously?

  • First, in the case of a single thread does not need to lock, for efficiency!
  • When an ArrayList is used as a local variable, it does not need to be locked because the local variable belongs to a thread. In the example above, ArrayList is used as a member variable. The collection of member variables needs to be shared by all threads.

7.5 How do I copy one ArrayList to another? How many can you name?

  • Use the clone() method, because ArrayList implements the Cloneable interface and can be cloned
  • Using the ArrayList constructor,ArrayList(Collection<? extends E> c)
  • useaddAll(Collection<? extends E> c)methods
  • Write your own loop to add() one by one

7.6 How can ArrayList Be Modified concurrently without exception?

Question: Given that the set of member variables stores N multiple user names, in a multi-threaded environment, how can iterators be used to read the set of data while still writing data to the set? Create a new thread task class:

/ * * *@Auther: csp1999
 * @Date: 2020/10/29/19:07
 * @Description: Thread task class that uses ArrayList to modify collection data in a multithreaded environment without concurrent modification exception */
public class CollectionThread implements Runnable{
    private static ArrayList<String> list = new ArrayList<>();
    static {
        list.add("Jack");
        list.add("Amy");
        list.add("Lucy");
    }

    @Override
    public void run(a) {
        for (String value : list){
            System.out.println(value);
            // Write data to collection while reading data
            list.add("Coco");// A concurrent modification exception occurs}}}Copy the code

Tests reading data from a shared collection while writing to it in multithreaded conditions:

/ * * *@Auther: csp1999
 * @Date: 2020/10/29 / whom *@Description: Interview question: * Given that the set of member variables stores N multiple user names, in a multithreaded environment, using iterators to read the set of data at the same time, * how to ensure the normal writing of data to the set? * /
public class Test03 {
    public static void main(String[] args) {
        // Create a thread task
        CollectionThread collectionThread = new CollectionThread();

        // Start 10 threads
        for (int i = 0; i < 10; i++) {
            newThread(collectionThread).start(); }}}Copy the code

Test results:

Jack
Jack
Jack
Jack
Jack
Jack
Jack
Jack
Jack
Jack
Exception in thread "Thread-0" Exception in thread "Thread-1" Exception in thread "Thread-4" Exception in thread "Thread-5" Exception in thread "Thread-8" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.haust.list.CollectionThread.run(CollectionThread.java:21)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "Thread-9" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.haust.list.CollectionThread.run(CollectionThread.java:21)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "Thread-2" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.haust.list.CollectionThread.run(CollectionThread.java:21)
	at java.lang.Thread.run(Thread.java:748)
java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.haust.list.CollectionThread.run(CollectionThread.java:21)
	at java.lang.Thread.run(Thread.java:748)
java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.haust.list.CollectionThread.run(CollectionThread.java:21)
	at java.lang.Thread.run(Thread.java:748)
java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.haust.list.CollectionThread.run(CollectionThread.java:21)
	at java.lang.Thread.run(Thread.java:748)
java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.haust.list.CollectionThread.run(CollectionThread.java:21)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "Thread-7" Exception in thread "Thread-6" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.haust.list.CollectionThread.run(CollectionThread.java:21)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "Thread-3" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.haust.list.CollectionThread.run(CollectionThread.java:21)
	at java.lang.Thread.run(Thread.java:748)
java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.haust.list.CollectionThread.run(CollectionThread.java:21)
	at java.lang.Thread.run(Thread.java:748)
Copy the code

Concurrent modification exceptions occur. To solve this problem, Java introduces a thread-safe read and write collection (read-write separation collection) : CopyOnWriteArrayList

So the solution is:

.// private static ArrayList<String> list = new ArrayList<>();
    // Replace the original ArrayList with read/write separated collections
    private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
    static {
        list.add("Jack");
        list.add("Amy");
        list.add("Lucy");
    }
    @Override
    public void run(a) {
        for (String value : list){
            System.out.println(value);
            // Write data to collection while reading data
            list.add("Coco");// A concurrent modification exception occurs}}Copy the code

Concurrent modification exception resolved successfully!

7.7 The difference between ArrayList and LinkedList?

  • ArrayList

    • Data structures based on dynamic arrays
    • Random access gets and sets are more efficient than linkedLists
    • ArrayList is not necessarily slower than LinkedList for random add and remove operations.
  • LinkedList

    • Data structures based on linked lists
    • For sequential operations, LinkedList is not necessarily slower than ArrayList
    • For random operations, LinkedList is significantly less efficient than LinkedList