Empty in ArrayList source code_ Elementdata and defaultprotocol_ EMPTY_ Differences between elementdata

JDK 1.8.0_ 162 empty in ArrayList source code_ Elementdata and defaultprotocol_ EMPTY_ Differences between elementdata

Words written before:

About reading source code: at the beginning of learning, I thought reading source code was so far away, but I unknowingly had graduated for a year, and I didn't make much progress. Hua Luogeng said, "self-study is not afraid of a low starting point, but not to the end". Reading the source code should be more "bottom", ha ha. Read the source code. When the interviewer asks you this question: "have you read the Java source code", you can pat him on the chest and answer him: "yes!!!". Last but not least: I have read the Java source code. (although I don't know how much I've gained)

To get back to business, Article 47 of the second edition of Effective Java: understanding and using class libraries has a saying: every programmer should be familiar with java.lang, java.util and, to some extent, the content in java.io. Then I started reading from Java. Util.

This article only discusses JDK 1.8.0_ Empty in 162_ Elementdata and defaultprotocol_ EMPTY_ For the difference between elementdata and the detailed interpretation of the source code, please Google.

About empty in ArrayList_ Elementdata (replaced by EE below) and defaultprotocol_ EMPTY_ The declaration of elementdata (replaced by DEE below) is defined as follows:

/**
 * Shared empty array instance used for empty instances.
 * Shared empty array instance for ArrayList empty instance
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 * Shared empty array instance for empty instances of default size. We will this (defaultprotocol_empty_elementdata)
 * And empty_ Element data is distinguished so that you know how much to expand when you add the first element.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

These two class constants EE and DEE represent empty arrays, but their names are different.

Three constructors:

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

/**
 * No parameter
 */
public ArrayList() {
	// here
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
 * Parameter is a collection
 */
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;
    }
}

The elementData of instance al created by parameterless constructor is DEE, and the elementData of empty instances al1 and al2 created by parameterless constructor is EE. Namely:

// elementData = DEE
ArrayList<String> al = new ArrayList<String>();

// elementData = EE
ArrayList<String> al1 = new ArrayList<String>(0);
ArrarList<String> al2 = new ArrayList<String>(al1)

Next, look at the add (E) method:

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

private static int calculateCapacity(Object[] elementData, int minCapacity) {
	// When the add (E) method is called for the first time, judge whether it is an object created by a parameterless constructor. If so,
	// Set DEFAULT_CAPACITY is 10 as the capacity of ArrayList. At this time, minCapacity = 1
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

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

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

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

Other add methods, such as add(int index, E element), addall (collection <? Extensions E > C) and addall (int index, collection <? Extensions E > C), all have the ensureCapacityInternal(int minCapacity) method to ensure that the minimum capacity of the instance al created by the nonparametric function is the default size of 10 when adding the first element. What about the empty instances al1 and al2 created by the parameter constructor when adding elements through add (E)? The capacity growth of al1 and al2 is like this: 0 - > 1 - > 2 - > 3 - > 4 - > 6 - > 9 - > 13... This growth is very slow. Specific expansion method:

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    // The new capacity is 1.5 times the old capacity
    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);
}

Question: both class constants represent empty arrays. Why use two? In Java 7, only one class constant represents an empty array, that is, EE. Java 8 adds DEE instead of EE.

In Java 7, the constructor of ArrayList:

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

public ArrayList() {
   super();
   this.elementData = EMPTY_ELEMENTDATA;
}

public ArrayList(Collection<? extends E> c) {
   elementData = c.toArray();
   size = elementData.length;
   // c.toArray might (incorrectly) not return Object[] (see 6260652)
   if (elementData.getClass() != Object[].class)
       elementData = Arrays.copyOf(elementData, size, Object[].class);
}

DEE completely replaced EE. What did EE do? Let's see where EE is arranged in the constructor? They are assigned to elementdata when it is judged that the capacity is empty. In Java 7, if the capacity is 0, an empty array will be created and assigned to elementData:this.elementData = new Object[initialCapacity] elementData = Arrays.copyOf(elementData, size, Object[].class);. If there are many empty ArrayList instances in an application, there will be many empty arrays. No doubt EE is to optimize performance. All empty ArrayList instances point to the same empty array. Problem solving.

Aside: Article 43 of the second edition of Effective Java: return an array or collection of zero length instead of null. Is it because this suggestion increases the number of empty instances of ArrayList, so the writer of the class library has made this optimization, ha ha.

Summary EMPTY_ELEMENTDATA and defaultprotocol_ EMPTY_ Difference between elementdata: EMPTY_ELEMENTDATA is to optimize the generation of unnecessary empty arrays when creating empty ArrayList instances, so that all empty ArrayList instances point to the same empty array. DEFAULTCAPACITY_EMPTY_ELEMENTDATA is to ensure that the instance created by the parameterless composition function has a minimum capacity of 10 by default when adding the first element.

Posted on Mon, 29 Nov 2021 05:37:27 -0500 by wsantos