2017-04-10 2 views
3

J'ai une liste avec un grand nombre d'éléments. Lors du traitement de cette liste, dans certains cas, je souhaite que la liste soit partitionnée en sous-listes plus petites et, dans certains cas, je souhaite traiter la liste entière.Java: Création de blocs de liste pour traitement

private void processList(List<X> entireList, int partitionSize) 
{ 
    Iterator<X> entireListIterator = entireList.iterator(); 
    Iterator<List<X>> chunkOfEntireList = Iterators.partition(entireListIterator, partitionSize); 
    while (chunkOfEntireList.hasNext()) { 
     doSomething(chunkOfEntireList.next()); 
     if (chunkOfEntireList.hasNext()) { 
      doSomethingOnlyIfTheresMore(); 
     } 
    } 

J'utilise com.google.common.collect.Iterators pour la création de partitions. Lien de documentation here Ainsi, dans le cas où je veux diviser la liste avec la taille 100, j'appelle

processList(entireList, 100); 

Maintenant, quand je ne veux pas créer des morceaux de la liste, je pensais que je pouvais passer entier. MAX_VALUE en tant que partitionSize.

processList(entireList, Integer.MAX_VALUE); 

Mais cela entraîne un manque de mémoire dans mon code. Est-ce que quelqu'un peut m'aider? Qu'est-ce que je rate? Que font les Iterators en interne et comment puis-je surmonter cela?

EDIT: J'ai également besoin de la clause "if" à l'intérieur pour faire quelque chose seulement s'il y a plus de listes à traiter. c'est-à-dire que j'ai besoin de la fonction hasNext() de l'itérateur.

Répondre

6

Vous obtenez une erreur de mémoire insuffisante car Iterators.partition() remplit en interne un tableau avec la longueur de partition donnée. Le tableau alloué est toujours la taille de la partition car le nombre réel d'éléments n'est pas connu avant la fin de l'itération. (La question aurait pu être évité si elles avaient utilisé un ArrayList interne, je suppose que les concepteurs ont décidé que les tableaux offriraient de meilleures performances dans le cas commun.)

L'utilisation Lists.partition() évitera le problème car il délègue à List.subList(), qui est seulement vue de la liste sous-jacente:

private void processList(List<X> entireList, int partitionSize) { 
    for (List<X> chunk : Lists.partition(entireList, partitionSize)) { 
     doSomething(chunk); 
    } 
} 
0

Normalement, lors du partitionnement, il alloue une nouvelle liste avec partitionSize donné. Il est donc évident dans ce cas qu'il y aura une telle erreur. Pourquoi n'utilisez-vous pas la liste d'origine lorsque vous n'avez besoin que d'une seule partition. Solutions possibles.

  1. créez une méthode séparée surchargée dans laquelle vous ne prendrez pas la taille.
  2. passez la taille -1 lorsque vous n'avez besoin d'aucune partition. Dans la méthode, vérifiez la valeur, si -1 puis mettez la liste originale dans le chunkOfEntireList,.
+0

J'ai envisagé de le faire. Mais cela rend le code super moche dans mon cas. Y a-t-il une autre solution que je peux essayer en dehors de Iterators.partition()? –

+0

vous pouvez créer une méthode de partiotion .. alors dans cette méthode, vous pouvez implémenter l'une des 2 solutions proposées. Il ne devrait pas rendre le code moche – stinepike

0

en supposant que vous essayez de résoudre le parallélisme en traitant des morceaux de votre liste en parallèle, il pourrait être préférable d'envisager quelque chose comme MapReduce, ou Spark comme un cadre plus large Cela inclut la gestion des processus.

Toutefois, dans le cadre d'une application monolithique, vous pouvez envisager des variantes de nœud local, y compris peut-être Java 8 Streams. Prenez note de la méthode parallelStream() également disponible sur votre List<X>.