Commençons par le cas général: l'utilisation d'un indicateur pour vérifier si un élément d'une collection remplit une certaine condition n'est pas rare. Mais le schéma que j'ai vu le plus souvent pour résoudre ce problème consiste à déplacer le chèque dans une méthode supplémentaire et à en revenir directement (comme Kilian Foth l'a décrit dans sa réponse ):
private <T> boolean checkCollection(Collection<T> collection)
{
for (T element : collection)
if (checkElement(element))
return true;
return false;
}
Depuis Java 8, il existe une manière plus concise d'utiliser Stream.anyMatch(…)
:
collection.stream().anyMatch(this::checkElement);
Dans votre cas, cela ressemblerait probablement à ceci (en supposant list == entry.getValue()
dans votre question):
map.values().stream().anyMatch(list -> list.size() > limit);
Le problème dans votre exemple spécifique est l'appel supplémentaire à fillUpList()
. La réponse dépend beaucoup de ce que cette méthode est censée faire.
Note latérale: En l'état, l'appel à fillUpList()
n'a pas beaucoup de sens, car il ne dépend pas de l'élément que vous êtes en train d'itérer. Je suppose que c'est une conséquence de la suppression de votre code réel pour l'adapter au format de la question. Mais cela donne exactement un exemple artificiel difficile à interpréter et donc difficile à raisonner. Par conséquent , il est si important de fournir un minimal, complet et Vérifiable exemple .
Je suppose donc que le code réel transmet le courant entry
à la méthode.
Mais il y a plus de questions à poser:
- Les listes de la carte sont-elles vides avant d'atteindre ce code? Si oui, pourquoi existe-t-il déjà une carte et pas seulement la liste ou le jeu de
BigInteger
clés? S'ils ne sont pas vides, pourquoi avez-vous besoin de remplir les listes? Lorsqu'il y a déjà des éléments dans la liste, n'est-ce pas une mise à jour ou un autre calcul dans ce cas?
- Qu'est-ce qui fait qu'une liste dépasse la limite? S'agit-il d'une condition d'erreur ou devrait-il se produire souvent? Est-ce causé par une entrée invalide?
- Avez-vous besoin des listes calculées jusqu'au point où vous atteignez une liste plus grande que la limite?
- Que fait la partie " Faire quelque chose "?
- Redémarrez-vous le remplissage après cette partie?
Ce ne sont que quelques questions qui me sont venues à l'esprit lorsque j'ai essayé de comprendre le fragment de code. Donc, à mon avis, c'est la véritable odeur de code : votre code ne communique pas clairement l'intention.
Cela peut signifier cela ("tout ou rien" et atteindre la limite indique une erreur):
/**
* Computes the list of all foo strings for each passed number.
*
* @param numbers the numbers to process. Must not be {@code null}.
* @return all foo strings for each passed number. Never {@code null}.
* @throws InvalidArgumentException if any number produces a list that is too long.
*/
public Map<BigInteger, List<String>> computeFoos(Set<BigInteger> numbers)
throws InvalidArgumentException
{
if (numbers.isEmpty())
{
// Do you actually need to log this here?
// The caller might know better what to do in this case...
logger.info("Nothing to compute");
}
return numbers.stream().collect(Collectors.toMap(
number -> number,
number -> computeListForNumber(number)));
}
private List<String> computeListForNumber(BigInteger number)
throws InvalidArgumentException
{
// compute the list and throw an exception if the limit is exceeded.
}
Ou cela peut signifier cela ("mise à jour jusqu'au premier problème"):
/**
* Refreshes all foo lists after they have become invalid because of bar.
*
* @param map the numbers with all their current values.
* The values in this map will be modified.
* Must not be {@code null}.
* @throws InvalidArgumentException if any new foo list would become too long.
* Some other lists may have already been updated.
*/
public void updateFoos(Map<BigInteger, List<String>> map)
throws InvalidArgumentException
{
map.replaceAll(this::computeUpdatedList);
}
private List<String> computeUpdatedList(
BigInteger number, List<String> currentValues)
throws InvalidArgumentException
{
// compute the new list and throw an exception if the limit is exceeded.
}
Ou ceci ("mettre à jour toutes les listes mais conserver la liste d'origine si elle devient trop grande"):
/**
* Refreshes all foo lists after they have become invalid because of bar.
* Lists that would become too large will not be updated.
*
* @param map the numbers with all their current values.
* The values in this map will be modified.
* Must not be {@code null}.
* @return {@code true} if all updates have been successful,
* {@code false} if one or more elements have been skipped
* because the foo list size limit has been reached.
*/
public boolean updateFoos(Map<BigInteger, List<String>> map)
{
boolean allUpdatesSuccessful = true;
for (Entry<BigInteger, List<String>> entry : map.entrySet())
{
List<String> newList = computeListForNumber(entry.getKey());
if (newList.size() > limit)
allUpdatesSuccessful = false;
else
entry.setValue(newList);
}
return allUpdatesSuccessful;
}
private List<String> computeListForNumber(BigInteger number)
{
// compute the new list
}
Ou même les suivants (en utilisant computeFoos(…)
le premier exemple mais sans exception):
/**
* Processes the passed numbers. An optimized algorithm will be used if any number
* produces a foo list of a size that justifies the additional overhead.
*
* @param numbers the numbers to process. Must not be {@code null}.
*/
public void process(Collection<BigInteger> numbers)
{
Map<BigInteger, List<String>> map = computeFoos(numbers);
if (isLimitReached(map))
processLarge(map);
else
processSmall(map);
}
private boolean isLimitReached(Map<BigInteger, List<String>> map)
{
return map.values().stream().anyMatch(list -> list.size() > limit);
}
Ou cela pourrait signifier quelque chose de complètement différent… ;-)