Collection removal methods can fail quietly in two distinct ways: a search or removal may receive an unrelated object, or List.remove may select the wrong overload. Both mistakes can leave the collection unchanged or remove an unintended element. section 8.1 section 8.9
Generics restrict most methods that add elements, but methods such as remove(Object), removeAll(), and retainAll() still accept broadly typed arguments. Passing an unrelated type usually produces no runtime error: removal removes nothing, while retainAll() may retain nothing. section 8.1
Removing by value or by index
List<Integer> offsets = getOffsets(); offsets.remove(0); On List<Integer>, the literal 0 binds to remove(int), so index 0 is removed. To remove the Integer value 0, write offsets.remove((Integer) 0). The Boolean-returning form can expose an unexpected absence: boolean success = offsets.remove((Integer) 0); assert success : "Element 0 wasn't found"; section 8.9
Reduce the risk of silent or ambiguous removals by applying these checks:
- Check that the searched object has the collection's semantic type, especially after migrating between File, Path, and String representations. section 8.1
- Use parameter-name hints in modern IDEs to verify whether List.remove is receiving an index or a value. section 8.9
- Check the boolean result from remove(Object), and assert success when the target is expected to exist. section 8.9
- Consider collections for boxed primitives from libraries such as FastUtil, which separate removal by value from removal by index. section 8.9
- Do not rely on static analysis alone: an ambiguous remove call may look valid because removal by index can be intentional. section 8.9
Set<File> allowedFiles = new HashSet<>();
boolean isAllowed(Path filePath) {
return allowedFiles.contains(filePath);
}
Compare the declared Set<File> element type with the Path argument; the containment test can quietly return false. section 8.1







