Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8158160

Change in behaviour of ArrayList iterator between JDK 6 and JDK 7

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Won't Fix
    • Icon: P4 P4
    • None
    • 7
    • core-libs
    • None

      Run the following test with JDK 6 and JDK 7. The output will differ on JDK 7,
      it will not print the "additionalItem". It is as if the additional item is not
      present.

      ---

      import java.util.*;

      /** A List that can be initialized with a given item. */
      public class ArrayListSubClass extends ArrayList<String> {

          private final String additionalItem;

          ArrayListSubClass(String additionalItems) {
              this.additionalItem = additionalItems;
          }

          @Override
          public int size() {
              return super.size() + 1;
          }

          @Override
          public String get(int index) {
              if (index < super.size()) {
                  return super.get(index);
              } else {
                  return additionalItem;
              }
          }

          public static void main(String[] args) {
              List<String> list = new ArrayListSubClass("additionalItem");
              list.add("first");
              list.add("second");
              list.add("third");

              int n = 0;
              for (Object o : list)
                  System.out.println(++n + " : " + o);
          }
      }


      $ jdk1.6.0_60/bin/java -version
      java version "1.6.0_60"
      Java(TM) SE Runtime Environment (build 1.6.0_60-b32)
      Java HotSpot(TM) 64-Bit Server VM (build 20.60-b02, mixed mode)

      $ jdk1.6.0_60/bin/java ArrayListSubClass
      1 : first
      2 : second
      3 : third
      4 : additionalItem

      $ jdk1.7.0_45/bin/java ArrayListSubClass
      1 : first
      2 : second
      3 : third

      ---

      This is a change in behavior between JDK 6 and JDK 7. The change in behavior was
      as a result of the changes in JDK-6359979, which was performance motivated.
      JDK 7 shipped in mid 2011, and to the best of my knowledge this is the first
      time that this issue has been reported. Extending ArrayList and relying on its
      abstract's supertype, AbstractList's, behavior is not recommended practice,
      though I do accept, looking at the JDK 6 API docs, that someone could get this
      impression [1], albeit misguided. The extender of ArrayList should NOT make any
      assumptions about its implementation.

      This is not something that we can realistically change again, as it could
      potentially have a negative affect on applications, running on JDK 7 and later,
      making assumptions about the present behavior and performance characteristics.
      Instead one should implement a composite iterator, see below for an example of
      this. Such an iterator will work for both JDK 6 and future JDK releases ( no
      JDK 7+ specific code is required ).

      ---

      The behavior that the testcase was relying on in JDK 6, namely that the
      implementation of ArrayList.iterator depends on the backing list's size(),
      get(int), and remove(int) methods, was part of the implementation specific notes
      in JDK 6 [1]. It is however, a binary compatible change to override a
      supertype's method in a subtype to provide an alternative, more efficient,
      implementation.

      The changes for JDK-6359979 should probably have had a release note in the
      compatibility guide for upgrading to JDK 7, but unfortunately they did not.

      ---

      Composite iterator that will work with the above testcase:

          @Override
          public Iterator<String> iterator() { return new InternalIterator(); }

          private class InternalIterator implements Iterator<String> {
              int cursor; // index of next element to return
              int lastRet = -1; // index of last element returned; -1 if no such
              int expectedModCount = modCount;

              public boolean hasNext() { return cursor != size(); }

              public String next() {
                  checkForComodification();
                  int i = cursor;
                  if (i >= size())
                      throw new NoSuchElementException();
                  cursor = i + 1;
                  lastRet = i;
                  return get(i);
              }

              public void remove() {
                  if (lastRet < 0)
                      throw new IllegalStateException();
                  checkForComodification();

                  try {
                      ArrayListSubClass.this.remove(lastRet);
                      cursor = lastRet;
                      lastRet = -1;
                      expectedModCount = modCount;
                  } catch (IndexOutOfBoundsException ex) {
                      throw new ConcurrentModificationException();
                  }
              }

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


      [1] http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html
          ( click on the iterator() link inherited from AbstractList )

            chegar Chris Hegarty
            chegar Chris Hegarty
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: