ก่อนปิดกรณีทั่วไป: การใช้ธงเพื่อตรวจสอบว่าองค์ประกอบบางส่วนของคอลเลกชันเป็นไปตามเงื่อนไขที่แน่นอนไม่ใช่เรื่องแปลก แต่รูปแบบที่ฉันเห็นบ่อยที่สุดในการแก้ปัญหานี้คือการย้ายเช็คในวิธีพิเศษและส่งคืนโดยตรงจากมัน (เช่น Kilian Foth อธิบายไว้ในคำตอบของเขา ):
private <T> boolean checkCollection(Collection<T> collection)
{
for (T element : collection)
if (checkElement(element))
return true;
return false;
}
ตั้งแต่ Java 8 มีวิธีการใช้ที่กระชับกว่าStream.anyMatch(…)
:
collection.stream().anyMatch(this::checkElement);
ในกรณีของคุณอาจมีลักษณะเช่นนี้ (สมมติlist == entry.getValue()
ในคำถามของคุณ):
map.values().stream().anyMatch(list -> list.size() > limit);
fillUpList()
ปัญหาในตัวอย่างเฉพาะของคุณคือการเรียกเพิ่มเติมเพื่อ คำตอบนั้นขึ้นอยู่กับว่าวิธีนี้ควรทำอย่างไร
หมายเหตุด้านข้าง: เนื่องจากการเรียกใช้fillUpList()
นั้นไม่สมเหตุสมผลนักเนื่องจากมันไม่ได้ขึ้นอยู่กับองค์ประกอบที่คุณกำลังทำซ้ำ ฉันเดาว่านี่เป็นผลมาจากการลอกรหัสจริงของคุณเพื่อให้พอดีกับรูปแบบคำถาม แต่สิ่งที่นำไปสู่ตัวอย่างเทียมที่ยากต่อการตีความจึงยากที่จะให้เหตุผล ดังนั้นจึงเป็นสิ่งสำคัญที่จะให้น้อยที่สุดสมบูรณ์และพิสูจน์ตัวอย่างเช่น
ดังนั้นฉันคิดว่ารหัสจริงผ่านกระแสentry
ไปยังวิธีการ
แต่มีคำถามเพิ่มเติมที่จะถาม:
- รายการในแผนที่ว่างเปล่าก่อนถึงรหัสนี้หรือไม่? ถ้าเป็นเช่นนั้นทำไมมีแผนที่อยู่แล้วไม่ใช่เฉพาะรายการหรือชุดของ
BigInteger
คีย์? หากรายการเหล่านั้นไม่ว่างทำไมคุณต้องเติมรายการ? เมื่อมีองค์ประกอบอยู่ในรายการอยู่แล้วนี่ไม่ใช่การอัปเดตหรือการคำนวณอื่น ๆ ในกรณีนี้ใช่ไหม
- อะไรทำให้รายการมีขนาดใหญ่กว่าขีด จำกัด นี่เป็นเงื่อนไขข้อผิดพลาดหรือคาดว่าจะเกิดขึ้นบ่อยครั้งหรือไม่ มันเกิดจากการป้อนข้อมูลที่ไม่ถูกต้อง?
- คุณต้องการรายการที่คำนวณจนถึงจุดที่คุณเข้าถึงรายการที่ใหญ่กว่าขีด จำกัด หรือไม่?
- ส่วน " ทำอะไรบางอย่าง " ทำอะไรได้บ้าง
- คุณเริ่มต้นการเติมหลังจากส่วนนี้หรือไม่?
นี่เป็นเพียงคำถามบางข้อที่อยู่ในใจของฉันเมื่อฉันพยายามที่จะเข้าใจส่วนของรหัส ดังนั้นในความคิดของฉันนั่นคือกลิ่นรหัสจริง : รหัสของคุณไม่ได้สื่อสารเจตนาอย่างชัดเจน
อาจหมายถึงสิ่งนี้ ("all or nothing" และถึงขีด จำกัด หมายถึงข้อผิดพลาด):
/**
* 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.
}
หรืออาจหมายถึงสิ่งนี้ ("อัปเดตจนถึงปัญหาแรก"):
/**
* 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.
}
หรือนี่ ("อัปเดตรายการทั้งหมด แต่เก็บรายการดั้งเดิมหากรายการใหญ่เกินไป")
/**
* 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
}
หรือแม้แต่ต่อไปนี้ (ใช้computeFoos(…)
จากตัวอย่างแรก แต่ไม่มีข้อยกเว้น):
/**
* 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);
}
หรืออาจหมายถึงบางสิ่งที่แตกต่างอย่างสิ้นเชิง… ;-)