2013-08-22 78 views
3

我使用谷歌gauva 11.0.1版,並有這段代碼:Immutable.listCopyfOf拋出ArrayIndexOutOfBoundsException異常

ImmutableList.copyOf(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,我可以通過首先獲取items集合的副本來解決問題。

+0

在這種情況下,「物品」是什麼?你能發佈一個簡短但完整的程序來證明問題嗎? –

+0

ConcurrentLinkedQueue的'size()'在不可變列表需要它的時候(用於創建數組並讀取值)不一定相同。 – kiheru

+0

嗨喬恩 - 我目前正試圖產生一個孤立的例子,再現這一點,並會發布如果當我可以做到這一點。如果有幫助,我也想升級到更高版本的番石榴。我懷疑它可能是由於試圖複製同時更新的Iterable。 – imrichardcole

回答

6

這似乎是您的集合的toArray()方法的問題。你說你正在使用一個ConcurrentLinkedQueue,但你的堆棧跟蹤顯示AbstractCollection.toArray。這似乎很腥,因爲java.util.ConcurrentLinkedQueue有自己的toArray實現。

你真的在使用什麼系列?我懷疑收集而不是ImmutableList

+0

立即檢查。 – imrichardcole

+0

很棒的地方。我在CLQ上找到了一個對Collections2.transform的調用。這意味着copyOf在TransformedCollection而不是CLQ上執行。一旦我找到更多,我會深入一點並更新問題(或回答它)。但TransformedCollection確實解釋了爲什麼我們看到AbstractCollection。 – imrichardcole

+4

是的,這可以解釋它。 'TransformedCollection'是底層'ConcurrentLinkedQueue'的實時視圖。轉換方法的javadoc說:「返回的集合不是線程安全的或可序列化的,即使fromCollection是」 – wolfcastle

0

在執行copyOf()的過程中,列表的大小是否可能發生變化?

如果在方法開始時檢查大小,但ConcurrentLinkedQueue大小增加,則會導致ArrayIndexOutOfBoundsException