Recognize ArrayList

Foreword

ArrayList frame set as a Java class the most commonly used, in general, it stores the set of data with the most suitable. Know these know why, in order to better understand and use ArrayList, this article from the following aspects in-depth understanding of ArrayList:

  • Why not array, with ArrayList
  • Source code analysis ArrayList properties
  • Java after 8 ArrayList
  • ArrayList using the correct posture

Why not an array, use ArrayList.

In the Java language, by the length of the array due to the general limitations when it is necessary to initialize the array defined length, according to the number of elements can not be dynamically expansion, and limited array of Java method call for developers, only taking element array length and obtain some simple additive element operating. Background in Java 1.2 introduces powerful rich Collection framework, which can be used as an ArrayList dynamic expansion of the array list implementation instead of using the daily development of Array, ArrayList implementation operation of all lists, enabling developers to easily operate the set list. Here, we first listed the main features under the ArrayList, be elaborated on later:

  • Orderly storage element

  • Allows repeated elements, allows storage of nullvalues
  • Support for dynamic expansion
  • Non-thread-safe

To better understand the ArrayList, we first look at the UML class diagrams from the ArrayList:

As can be seen from the figure ArrayList inherited AbstractList, directly implements Cloneable, Serializable, RandomAccess type flag interface.

  • AbstractList as an abstract list of implementation, the additions and deletions to change search elements are handed over to specific sub-class to implement, it provides a default implementation iterates over the elements in the operation.
  • Implement the Cloneable interface, it expressed support for the call of Object ArrayList clonemethod to achieve copy of the ArrayList.
  • Serializable interface, the described sequence also supports ArrayList and deserialization operation, having a fixed serialVersionUIDproperty value.
  • RandomAccess interface, represents the ArrayList, the elements can be accessed randomly access elements of effective efficiency, the following standard digital way. RandomAccess list implemented interface can be used directly in the normal traversal forcycle mode, and to perform the iterator more efficient manner.

ArrayList source code analysis

ArrayList into the source code, from the structure of the class's members soon see two important variables of ArrayList: elementDataand size.

  • elementDataObject is an array of storage elements, it is required outside into the elements of the ArrayList that ArrayList object maintains this array of objects Object [], provide external CRUD and traverse are related to this array, and thus added to ArrayList elements are stored in an orderly array object elementDatain.
  • sizeField indicates the number of elements currently added to the ArrayList, it should be noted that the object must be less than equal to the array elementDatalength. Once when sizethe elementDatasame length, and also when the additive element to the list, the ArrayList expansion operation will be executed, with a longer array elements previously stored objects.

As the underlying maintain an array of objects, the element added to the collection of natural ArrayList can be repeated, it is allowed null, and their respective index position is not the same.

How expansion

End ArrayList understand why orderly storage elements and element can be repeated, we will look at as a dynamic array list, how the underlying expansion is achieved.

First, to determine when the next opportunity where the expansion is, as described above, sizementioned at the field, when sizethe elementDatasame length, at the moment add an element to the set there will not be enough capacity, the need for expansion, i.e. ArrayList expansion adding process operation occurs, and occurs only when certain conditions are met.

Let us now look at the code structure ArrayList class can be seen there are four ways to add elements, divided into two categories: adding a single element and adding all the elements in the other set.

Start with a simple way to start analysis, see add(E):booleanways:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e; 
    return true;
}

As can be seen from the above third line is to simply add a single element, and so sizeincremented by 1; then implemented in the expansion ensureCapacityInternalprocess, where its argument is size+1 is added to the element is determined before the actual additive element number, i.e. set the required minimum capacity if more than the original length of the array. Look at this ensureCapacityInternalway to achieve

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData,minCapacity));
}

Inside there are two method calls, first of all look relatively simple calculateCapacitymethod:

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

When elementDatathe DEFAULTCAPACITY_EMPTY_ELEMENTDATAequal array is empty, a return element may be added default minimum capacity value DEFAULT_CAPACITYcorresponding to 10, or according to incoming size+1 minimum capacity value; then look after the execution of ensureExplicitCapacitythe method:

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

    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

Expansion can be seen from the code implemented growin the method, and performed only when the array length is less than the minimum capacity required: an array of storage elements when full and can not store the newly added element.

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

Jump to the further growimplementation of the method, see line 8 using the method of tools java.util.Arrays#copyOf(T[], int), copying the original array, all of the internal element at the length newCapacityof the new array, and the corresponding references to the assignment of the new array elementData. ArrayList inside the object now is updated with the new reference of the length of the array, the effect is achieved as shown below:

Now let's look at the representative of the array of new capacity newCapacityis adjusted to the number. First, newCapacityby oldCapacity + (oldCapacity >> 1)obtained by calculation using bit arithmetic original capacity value oldCapacitythrough the right one, is obtained half value (rounded down), then add the capacity of the original value, then the value of the original capacity is oldCapacity1.5 times.

>>Right Bitwise operators, will be left to the right operand, divided by the equivalent of 2, and rounding down, such as the expression (7 >> 1) == 3is true.

When the calculated newCapacitytime is still smaller than the minimum capacity value passed to indicate the current number of the array is empty, the default DEFAULT_CAPACITYallocation array as capacity value.

Note that there is an additional determination of the maximum number of the array, MAX_ARRAY_SIZEthe code in the file corresponding to the following definitions:

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

ArrayList storage element has the largest number of restrictions, if over the limit will cause the JVM to throw an OutOfMemoryErrorexception.

To this java.util.ArrayList#add(E)expansion a logical method of analysis to an end. Similarly, in other ways to achieve within the added elements where we can see ensureCapacityInternalthe called method will be confirmed before the actual operating capacity of the underlying array, the capacity is not enough for dynamic expansion.

Serialization and de-serialization

transient Object[] elementData;

ArrayList seen in the source elementDatawith the keyword transient, usually transientkeyword modified fields indicate that the fields are not serialized, but ArrayList implements serialization interface, and the sequence of the method provided by writeObjectthe deserialization method readObjectimplemented, this is how to do it?

We first look at the ArrayList sequence of code:

private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
    int expectedModCount = modCount;
    s.defaultWriteObject();

    s.writeInt(size);

    for (int i = 0; i < size; i++) {
        s.writeObject(elementData[i]);
    }

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

4 first line of code of the current object non static-modified, non transient-modified field is written to the stream; 6 about to write a number of elements as the capacity.

The next step is to flow through the write cycle will contain all the elements, this step can be seen in the ArrayList no no memory space for storing data for serialization, saves space and time in the sequence of their method of implementation.

Similarly, the deserialization acquired according to the incoming stream of data read sizeattribute, then the expansion of the array, and finally all the elements stored in the data stream to read the data held in the array of objects.

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

    s.defaultReadObject();

    s.readInt(); // ignored

    if (size > 0) {
        int capacity = calculateCapacity(elementData, size);
        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
        ensureCapacityInternal(size);

        Object[] a = elementData;
        for (int i = 0; i < size; i++) {
            a[i] = s.readObject();
        }
    }
}

About copy

For a copy of the list of elements, ArrayList provide a custom clone achieve the following:

public Object clone() {
  try {
    ArrayList<?> v = (ArrayList<?>) super.clone();
    v.elementData = Arrays.copyOf(elementData, size);
    v.modCount = 0;
    return v;
  } catch (CloneNotSupportedException e) {
    // this shouldn't happen, since we are Cloneable
    throw new InternalError(e);
  }
}

As apparent from the above code that perform copyOfoperations is a shallow copy operations, elements ArrayList original copy of the object will not be saved to a new ArrayList object and then return to their respective fields elementDatain the respective storage position is the same elements reference, which once modified a list of elements in the array, another list will also be affected.

After the JDK 1.8 ArrayList

After complete analysis of the characteristics of ArrayList from the source point of view, we look at what's new JDK 1.8 after changes in the ArrayList class.

New method removeIf

removeIfCollection interface is a new interface method, since the ArrayList parent class implements the interface, so have the methods. removeIfMethods for performing the specified conditions to remove elements from the array.

public boolean removeIf(Predicate<? super E> filter){...}

A condition representative of the incoming interface function parameters Predicate, i.e. Lambda expressions matching conditions, if the condition is true, the element is removed from the array, for example, sample code below:

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
numbers.removeIf(i -> i % 2 == 0);
System.out.println(numbers); // [1, 3, 5, 7, 9]

New method spliterator

This method is also from the Collection interface, ArrayList this method has been rewritten. The method returns ListSpliterator instance, which is used to traverse element separation vessel and stored.

@Override
public Spliterator<E> spliterator() {
    return new ArrayListSpliterator<>(this, 0, -1, 0);
}

ArrayList in the implementation of the internal static method returns a class object ArrayListSpliterator, which can be operated by a set of elements can.

Its main operation of the following three ways:

  • tryAdvance Iteration single element, similar to iterator.next()
  • forEachRemaining Iteration remaining elements
  • trySplit The element is cut into two parts in parallel, but note Spliterator not thread safe.

Although this method is not commonly used three, still need to understand, you can simply look at ways of using

ArrayList<Integer> numbers = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));
Spliterator<Integer> numbers = numbers.spliterator();

numbers.tryAdvance( e -> System.out.println( e ) ); // 1

numbers.forEachRemaining( e -> System.out.println( e ) ); // 2 3 4 5 6

Spliterator<Integer> numbers2 = numbers.trySplit();

numbers.forEachRemaining( e -> System.out.println( 3 ) );      //4 5 6
numbers2.forEachRemaining( e -> System.out.println( 3 ) );      //1 2 3

We will certainly use gestures

After contact with the source ArrayList and new API, we finally learn how to use ArrayList efficiently in normal development.

Efficient initialization

ArrayList implements three constructors, is assigned to an empty array objects created by default EMPTY_ELEMENTDATA; the second is passed to initialize a data collection type; allow incoming third initialization value set length, i.e. the length of the array. Because each array length is not enough capacity expansion will lead to re-apply for a longer memory and copy it. And let us initialize the array specified initial size ArrayList, you can reduce the number of expansion array, providing performance.

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

Traversing element

Before JDK 1.8, ArrayList traverse only supports three ways: iterates over ordinary forcycle, for-eachafter enhancement, in JDK1.8 introduced Stream API, belong to the same set ArrayList Collection, may be used stream.foreach()a method to obtain a number of elements:

ArrayList<String> names = new ArrayList<String>(Arrays.asList( "alex", "brian", "charles"));
names.forEach(name -> System.out.println(name)); // alex brian charles

Conversion Array

ArrayList list provides two methods for converting the array

public Object[] toArray();
public <T> T[] toArray(T[] a);
  1. The first method returns an array of type Object
  2. In the second method, the return type of the array is passed specified array type. If the length of the list and is passed in line array, the array element after the copy, which is returned in the array. Otherwise, a new array will be reallocated according to the size and type of the list of incoming array, and then copy the completed return.

As can be seen from the above described second method is more appropriate to retain the original type:

ArrayList<String> list = new ArrayList<>(4);
list.add("A");
list.add("B");
list.add("C");
list.add("D");

String[] array = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(array)); // [A, B, C, D]

Deal with multithreading

It should be noted here ArrayList itself not thread-safe, thread-safe manner if required list is generally employed java.util.Collections#synchronizedList(java.util.List<T>)or using the Vector class instead. Another way is to use a container class CopyOnWriteArrayList concurrent use of multiple threads, it is achieved by creating a copy of the underlying array of original update, add, etc. had to be synchronized operation, not only thread-safe, reducing the synchronization of threads.

Additions and deletions to deal with the head node

ArrayList is an array implemented using a continuous memory space, when there are add or delete elements in the array of the head, the need for subsequent header data replication and re-ordering, inefficient. For a large number of scenes similar operations, for performance reasons, we should use LinkedList instead. Since LinkedList is based on the linked list implementation, when the position of the element to be operated on the front half of the List, to traverse from the beginning, you will immediately find the elements inserted in the corresponding position, or delete operation.

Epilogue

Here we learn to summarize and realize common use ArrayList as a basis for the collection container, the more we have a better understanding, the more smoothly for our daily use. Due to the above-mentioned list another set of LinkedList, it realized in different ways, using different scenes, as the next article will analyze the collection stage, a small partner interested welcome attention to my micro-channel public number, look forward to updating and ArrayList.

reference

  • https://www.cnblogs.com/skywang12345/p/3308556.html
  • https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html
  • https://yuqirong.me/2018/01/21/ArrayList%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/
  • https://juejin.im/post/5a58aa62f265da3e4d72a51b
  • https://howtodoinjava.com/java-arraylist/
  • http://cmsblogs.com/?p=4727

Guess you like

Origin www.cnblogs.com/one12138/p/11456147.html