En primer lugar, el caso general: no es raro usar una bandera para verificar si algún elemento de una colección cumple una determinada condición. Pero el patrón que he visto con mayor frecuencia para resolver esto es mover el cheque en un método adicional y regresar directamente de él (como lo describió Kilian Foth en su respuesta ):
private <T> boolean checkCollection(Collection<T> collection)
{
for (T element : collection)
if (checkElement(element))
return true;
return false;
}
Desde Java 8 hay una manera más concisa usando Stream.anyMatch(…)
:
collection.stream().anyMatch(this::checkElement);
En su caso, esto probablemente se vería así (suponiendo list == entry.getValue()
en su pregunta):
map.values().stream().anyMatch(list -> list.size() > limit);
El problema en su ejemplo específico es la llamada adicional a fillUpList()
. La respuesta depende mucho de lo que se supone que debe hacer este método.
Nota al margen: tal como está, la llamada a fillUpList()
no tiene mucho sentido, porque no depende del elemento que está iterando actualmente. Supongo que esto es una consecuencia de eliminar su código real para que se ajuste al formato de la pregunta. Pero exactamente eso lleva a un ejemplo artificial que es difícil de interpretar y, por lo tanto, difícil de razonar. Por lo tanto, es tan importante proporcionar un ejemplo mínimo, completo y verificable .
Así que supongo que el código real pasa la corriente entry
al método.
Pero hay más preguntas que hacer:
- ¿Están vacías las listas en el mapa antes de llegar a este código? Si es así, ¿por qué ya hay un mapa y no solo la lista o el conjunto de
BigInteger
claves? Si no están vacíos, ¿por qué necesita completar las listas? Cuando ya hay elementos en la lista, ¿no es una actualización o algún otro cálculo en este caso?
- ¿Qué causa que una lista sea más grande que el límite? ¿Es esta una condición de error o se espera que ocurra con frecuencia? ¿Es causado por una entrada no válida?
- ¿Necesita las listas calculadas hasta el punto en que alcanza una lista más grande que el límite?
- ¿Qué hace la parte " Hacer algo "?
- ¿Reinicia el relleno después de esta parte?
Estas son solo algunas preguntas que me vinieron a la mente cuando intenté entender el fragmento de código. Entonces, en mi opinión, ese es el verdadero olor del código : su código no comunica claramente la intención.
Podría significar esto ("todo o nada" y alcanzar el límite indica un error):
/**
* 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.
}
O podría significar esto ("actualizar hasta el primer problema"):
/**
* 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.
}
O esto ("actualice todas las listas pero mantenga la lista original si se vuelve demasiado 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
}
O incluso lo siguiente (usando computeFoos(…)
el primer ejemplo pero sin excepciones):
/**
* 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);
}
O podría significar algo completamente diferente ... ;-)