-
Bug
-
Resolution: Won't Fix
-
P4
-
None
-
7
-
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 inJDK-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 forJDK-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 )
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 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
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 )
- relates to
-
JDK-8157110 Behavioural change with extended ArrayList class
-
- Closed
-
-
JDK-6359979 (coll) Speed up collection iteration
-
- Closed
-