2013-08-22 3 views
3

J'utilise google version Gauva 11.0.1 et ont ce morceau de code:Immutable.listCopyfOf jette ArrayIndexOutOfBoundsException

ImmutableList.copyOf(items); 

Lorsque des articles est une ConcurrentLinkedQueue. Je vois parfois cette erreur:

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) 

Étant donné que le problème réside entièrement dans la bibliothèque de goyave, quelqu'un sait-il pourquoi?

Mise à jour sur la base réponse ci-dessous

Merci pour l'aide de Wolfcastle, je suis parvenu à reproduire le problème en vase clos, en dehors de ma demande.

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); 

L'exécution de ce produit le suivant à chaque fois:

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) 

Pour résoudre dans ma demande, j'ai trouvé un certain nombre d'options.

Se éloignant de Collections2

En passant de Collections2.transform à Iterables.transform, le problème disparaît.

Déplacement loin de Java 1.5

Bien que cela n'a pas été possible dans ma situation, je l'ai essayé avec Java 1.6 et Java 1.7 et le problème est résolu. Je soupçonne que cela est dû à un changement de mise en œuvre AbstractCollection.toArray() de 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; 
} 

Copie du ConcurrentLinkedQueue premier

Exécution une transformation sur une collection non-thread-thread est évidemment loin d'être idéale. Si pour une raison quelconque je devais rester avec Collections2.transform, je peux résoudre le problème en prenant une copie de la collection d'items en premier.

+0

Qu'est-ce que 'items' dans ce cas?Pouvez-vous poster un programme court mais complet démontrant le problème? –

+0

Le paramètre 'size()' de la propriété 'ConcurrentLinkedQueue' n'est pas nécessairement le même pendant toute la durée de la liste immuable (pour créer le tableau et lire les valeurs). – kiheru

+0

Salut Jon - J'essaye actuellement de produire un exemple isolé qui reproduit ceci et posterai si et quand je peux faire cela. Je suis également tenté de passer à une version plus récente de la goyave, au cas où cela serait utile. Je me doute qu'il peut être dû à essayer de copier un Iterable qui est mis à jour en même temps. – imrichardcole

Répondre

6

Cela semble être un problème avec la méthode toArray() de votre collection. Vous dites que vous utilisez une file d'attente ConcurrentLinkedQueue, mais votre trace de pile affiche AbstractCollection.toArray. Cela semble louche puisque java.util.ConcurrentLinkedQueue a sa propre implémentation de toArray.

Quelle collection utilisez-vous vraiment? Je suspecte cette collection et non ImmutableList.

+0

Vérification de ceci maintenant. – imrichardcole

+0

Super endroit. J'ai trouvé un appel à Collections2.transform sur le CLQ. Cela signifie alors que le copyOf est effectué sur un TransformedCollection et non le CLQ. Je vais creuser un peu plus et mettre à jour la question (ou y répondre) une fois que j'en ai trouvé plus. Mais TransformedCollection explique en effet pourquoi nous voyons le AbstractCollection. – imrichardcole

+4

Oui, ça l'expliquerait. 'TransformedCollection' est une vue en direct du' ConcurrentLinkedQueue' sous-jacent. Le javadoc pour la méthode de transformation dit "La collection retournée n'est pas threadsafe ou sérialisable, même si fromCollection est" – wolfcastle

0

Est-il possible que la taille de la liste change pendant l'exécution de copyOf()?

Si la taille est vérifiée au début de la méthode, mais que la taille ConcurrentLinkedQueue est augmentée, cela provoquera un ArrayIndexOutOfBoundsException.

Questions connexes