web-dev-qa-db-fra.com

Comment refactoriser cette boucle?

J'ai une application où j'utilise des tableaux et des listes primitifs pour une classe appelée Item. Ceux-ci sont utilisés de manière interchangeable pour des raisons héritées (je souhaite également que ce ne soit qu'un type, mais c'est ainsi).

Maintenant, je dois ajouter une nouvelle méthode comme celle-ci qui fonctionne via une boucle pour chaque:

public void something(Item... items) {
    for (Item i : items) {
        doStuff();
    }
}

public void something(List<Item> items) {
    for (Item i : items) {
        doStuff();
    }
}

En d'autres termes, exactement la même méthode deux fois pour les tableaux et les listes primitifs. Existe-t-il un moyen de refactoriser correctement cela en une seule méthode?

46
Selbi

Vous ne peut pas ne devrait pas (*) faire cela dans une seule méthode. Item[] Et List<Item> Sont des types indépendants.

Vous devez faire appel à l'une des surcharges: something(Item... items) appelle something(List<Item>) ou something(List<Item>) appelle something(Item... items).

Parmi les deux options, il est préférable que la surcharge du tableau appelle la surcharge de liste:

public void something(Item... items) {
  something(Arrays.asList(item));
}

C'est bon marché, car il ne copie pas le tableau, mais l'enveloppe plutôt: la création de List est O(1).

Si vous deviez invoquer la surcharge du tableau à partir de la surcharge de la liste:

public void something(List<Item> items) {
  something(items.toArray(new Item[0]));
}

Ce serait plus cher, puisque l'appel toArray doit créer et remplir un tableau: c'est une opération O(n), où n est la taille de la liste. Cependant, il a le léger avantage que something ne serait pas en mesure de remplacer le contenu de List, car les mises à jour du tableau sont simplement ignorées après l'exécution.


(*) Vous pouvez, mais ce serait vraiment grossier et non sûr pour le type, car vous devez accepter un paramètre Object, car il n'y a pas d'autre super type commun de List<Item> et Item[]; et vous finiriez toujours par devoir répéter les boucles pour les deux types; et vous devez gérer la possibilité de passer un type complètement indépendant (au moment de l'exécution):

public void something(Object obj) {
  if (obj instanceof List) {
    for (Object element : (List<?>) obj) {
      Item item = (Item) element;  // Potential ClassCastException.
      doStuff();
    }
  } else if (obj instanceof Item[]) {
    for (Item item : (Item[]) obj) {
      doStuff();
    }
  } else {
    throw new IllegalArgumentException();
  }
}

Quel bordel. Merci au fabricant pour les surcharges.

68
Andy Turner

Si vous utilisez Java 8, vous pouvez aussi simplement appeler forEach ou map sur votre - Stream et vous avez terminé, par exemple.

yourStream.forEach(doStuff());

doStuff() est le consommateur traitant de la chaîne ou utilisez yourStream.forEach(s -> doStuff()) si vous ne voulez pas gérer la chaîne et juste do stuff.

Vous pouvez obtenir un flux comme suit:

Stream.of(yourArray) // or Arrays.stream(yourArray)
      .forEach(doStuff());

et pour votre liste:

list.stream()
    .forEach(doStuff());

Le principal avantage de l'utilisation des flux est probablement la lisibilité. Il peut perdre en termes de performances et peut également perdre si vous ne voulez pas appeler Stream.of/Arrays.stream Ou Collection.stream() juste pour obtenir le flux.

Si vous voulez vraiment garder la méthode something(...) (être capable de gérer les deux: les varargs et la liste) vous avez toujours besoin d'une méthode surchargée ou utilisez la proposition d'Andy Turner avec le Object- méthode-paramètre.

17
Roland

Vous pouvez implémenter une seule méthode, dans ce cas, la seconde car elle a une liste en paramètre. Au lieu de la première méthode, vous pouvez convertir le tableau dans une liste avec Arrays.asList(items), puis vous pouvez appeler la première méthode. Donc, au final, vous n'aurez qu'une seule méthode (qui a une liste en paramètre).

De plus, si la liste des éléments contient peu d'éléments, vous pouvez utiliser les expressions lambda de Java 8:

items.foreach(item -> doStuff(item));

Ainsi, vous n'aurez pas de méthode qui ne contienne qu'une seule boucle et le code sera plus facile à lire.

1
eve2017

Vous devriez y parvenir en passant List après l'avoir converti en tableau.

Conservez cela comme votre méthode unique,

public void something(Item... items) {
   for (Item i : items) {
       doStuff();
   }
}

et quand vous voulez passer un List<Item> alors passez comme ça,

something(listItem.toArray(new Item[listItem.size()]))

0
jack jay