Domanda

So, pop()method in java framework java.util.Stack class looks like this:

@SuppressWarnings("unchecked")
public synchronized E pop() {
    if (elementCount == 0) {
        throw new EmptyStackException();
    }
    final int index = --elementCount;
    final E obj = (E) elementData[index];
    elementData[index] = null;
    modCount++;
    return obj;
}

The part that I have trouble in understanding is local variable index. It seems we don't need it. elementCount is a instance variable in Vector class which Stack class extended.


So my point is,

    final int index = --elementCount;
    final E obj = (E) elementData[index];
    elementData[index] = null;

These 3 lines of code can be written like

    final E obj = (E) elementData[--elementCount];
    elementData[elementCount] = null;

which consumes less memory, because memory space for index local variable isn't used.

Also, I found this pattern along the java framework source code. For example add(E Object) method in java.util.ArrayList class looks :

@Override public boolean add(E object) {
    Object[] a = array;
    int s = size;
    if (s == a.length) {
        Object[] newArray = new Object[s +
                (s < (MIN_CAPACITY_INCREMENT / 2) ?
                 MIN_CAPACITY_INCREMENT : s >> 1)];
        System.arraycopy(a, 0, newArray, 0, s);
        array = a = newArray;
    }
    a[s] = object;
    size = s + 1;
    modCount++;
    return true;
}

in this example, array is a instance variable, and as you can see, a new local variable a is assigned to hold it.

Does anybody know about this? Big Thanks in advance. :)

È stato utile?

Soluzione

Though this is a really old question, but I want to share some information I earned during my journey.

I could find some explanation about my question on Performance Tips on Android page. First see sample code from the page,

static class Foo {
    int mSplat;
}

Foo[] mArray = ...

public void zero() {
    int sum = 0;
    for (int i = 0; i < mArray.length; ++i) {
        sum += mArray[i].mSplat;
    }
}

public void one() {
    int sum = 0;
    Foo[] localArray = mArray;
    int len = localArray.length;

    for (int i = 0; i < len; ++i) {
        sum += localArray[i].mSplat;
    }
}

public void two() {
    int sum = 0;
    for (Foo a : mArray) {
        sum += a.mSplat;
    }
}

According to the above page, zero() is slowest, one() is faster. Because it pulls everything out into local variables, avoiding the lookups.

I think this explanation might solve my second question, which was asking "a new local variable a is assigned to hold it. but why?"

I hope this might help someone who have the same curiosity.


[EDIT] Let me add some details about "lookups".

So if you compile above code and disassembles the class file with javap command with -c option, it will print out disassembled code, i.e., the instructions that comprise the Java bytecodes.

public void zero();
Code:
   0: iconst_0                          // Push int constant 0
   1: istore_1                          // Store into local variable 1 (sum=0)
   2: iconst_0                          // Push int constant 0
   3: istore_2                          // Store into local variable 2 (i=0)
   4: goto          22                  // First time through don't increment
   7: iload_1
   8: aload_0
   9: getfield      #14                 // Field mArray:[LTest$Foo;
  12: iload_2
  13: aaload
  14: getfield      #39                 // Field Test$Foo.mSplat:I
  17: iadd
  18: istore_1
  19: iinc          2, 1
  22: iload_2                           // Push value of local variable 2 (i)
  23: aload_0                           // Push local variable 0 (this)
  24: getfield      #14                 // Field mArray:[LTest$Foo;
  27: arraylength                       // Get length of array
  28: if_icmplt     7                   // Compare and loop if less than (i < mArray.length)
  31: return

public void one();
Code:
   0: iconst_0                          // Push int constant 0
   1: istore_1                          // Store into local variable 1 (sum=0)
   2: aload_0                           // Push this
   3: getfield      #14                 // Field mArray:[LTest$Foo;
   6: astore_2                          // Store reference into local variable (localArray)
   7: aload_2                           // Load reference from local variable
   8: arraylength                       // Get length of array
   9: istore_3                          // Store into local variable 3 (len = mArray.length)
  10: iconst_0                          // Push int constant 0
  11: istore        4                   // Store into local variable 4 (i=0)
  13: goto          29                  // First time through don't increment
  16: iload_1
  17: aload_2
  18: iload         4
  20: aaload
  21: getfield      #39                 // Field Test$Foo.mSplat:I
  24: iadd
  25: istore_1
  26: iinc          4, 1
  29: iload         4                   // Load i from local variable
  31: iload_3                           // Load len from local variable
  32: if_icmplt     16                  // // Compare and loop if less than (i < len)
  35: return

These instructions are a bit unfamiliar, so I looked up in JVM spec documents. (If you are curious, especially chapter 3, Compiling for the Java Virtual Machine, and chapter 6, The Java Virtual Machine Instruction Set would be helpful).

I added comment to help you understand, but in a nut shell, method zero() should operate getfield instruction on every iteration. According to JVM spec documentation 3.8. Working with Class Instances section, getfield operation performs several jobs like below.

The compiler generates symbolic references to the fields of an instance, which are stored in the run-time constant pool. Those run-time constant pool items are resolved at run-time to determine the location of the field within the referenced object.

Altri suggerimenti

These 3 lines of code can be written like

We're in the business of making a useful and extendable programs, and in order to acheive that we should make our life as Developers easy as we can.
If it takes me a 5 more seconds to read the code and i can simplify it, i would. Specially if it comes in the expense of a int memory.. hardly calls as Optimization.

in this example, array is a instance variable, and as you can see, a new local variable a is assigned to hold it. Does anybody know about this?

This is hardly calls as question, i believe you meant to phrase it like that:
Why does they used another reference to array called a if they could use array ?

Well, I truly can't see why, because they could have use the E type since it given to them. It may be a reason of Covariance and Contravariance but i'm not sure.

Tip: Also next time you add pieces of a language source code, it will be nice to know which JDK you are viewing and a link me very help.

Keep in mind that --elementCount does the assignment before decrement. That means the fragment:

final int index = --elementCount;
final E obj = (E) elementData[index];
elementData[index] = null;

Can be translated into

final int index = elementCount;
elementCount--;
final E obj = (E) elementData[index];
elementData[index] = null;

Which means in your proposed replacement "elementData[--elementCount]" and "elementData[elementCount]" do not reference the same item. Your proposed replacement is not equivalent. Hope this helps.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top