私はGoogle Gauvaバージョン11.0.1を使用しており、次のコードがあります:
ImmutableList.copyOf(items);
items は ConcurrentLinkedQueue です。ときどき次のエラーが表示されます。
java.lang.ArrayIndexOutOfBoundsException: 10
at java.util.AbstractCollection.toArray(AbstractCollection.java:126)
at com.google.common.collect.ImmutableList.copyFromCollection(ImmutableList.java:278)
at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:247)
at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:217)
問題が完全にグアバライブラリ内にあることを考えると、その理由を知っている人はいますか?
以下の正解に基づいて更新
wolfcastle の助けを借りて、アプリケーションの外部で問題を分離して再現することができました。
final int itemsToPut = 30000;
final ConcurrentLinkedQueue<Integer> items = new ConcurrentLinkedQueue<Integer>();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < itemsToPut; i++) {
items.add(i);
}
}
}, "putter-thread").start();
final Iterable<String> transformed = Collections2.transform(items, new Function<Integer, String>() {
public String apply(Integer integer) {
return "foo-" + integer;
}
});
ImmutableList.copyOf(transformed);
これを実行すると、毎回次が生成されます。
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 21480
at java.util.AbstractCollection.toArray(AbstractCollection.java:126)
at com.google.common.collect.ImmutableList.copyFromCollection(ImmutableList.java:278)
at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:247)
at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:217)
私のアプリケーションで解決するために、いくつかのオプションを見つけました。
Collections2 からの移行
Collections2.transform から Iterables.transform に切り替えると、問題はなくなります。
Java 1.5 からの移行
これは私の状況では不可能でしたが、Java 1.6 と Java 1.7 で試してみたところ、問題は解決しました。これは、AbstractCollection.toArray() の実装が 1.5 から変更されたためだと思われます。
1.5
public Object[] toArray() {
Object[] result = new Object[size()];
Iterator<E> e = iterator();
for (int i=0; e.hasNext(); i++)
result[i] = e.next();
return result;
}
1.6
public Object[] toArray() {
// Estimate size of array; be prepared to see more or fewer elements
Object[] r = new Object[size()];
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) // fewer elements than expected
return Arrays.copyOf(r, i);
r[i] = it.next();
}
return it.hasNext() ? finishToArray(r, it) : r;
}
最初に ConcurrentLinkedQueue をコピーする
スレッドセーフでないコレクションで変換を実行することは、明らかに理想からはほど遠いものです。何らかの理由で Collections2.transform を使用しなければならない場合は、最初にアイテム コレクションのコピーを取得することで問題を解決できます。