Definition
- remove(int index) removes the element at the specified position in the list.
- remove(Object o) removes the first occurrence of the specified element by value.
- When the argument is a primitive
intorshort(widened toint), Java resolves toremove(int index)because an exactintoverload exists. - Autoboxing to
Short/Integerdoes NOT happen during overload resolution when anintoverload exists. - To force value removal, explicitly box:
list.remove(Short.valueOf(i))or cast:list.remove((Short) (short) i). - The safest alternative is
removeIf(x -> x == target)which has no overload ambiguity.
You add cheque numbers 1, 2, 3 to a box. You say "remove 1". Did you mean remove cheque number 1 (by value) or remove the cheque at position 1 (by index)? Java picks "by index" when the argument is int, and "by value" when it is a Short object. This ambiguity causes the surprise.
Understanding the Concepts
What is the autoboxing trap?
List(andList) has tworemovemethods:remove(int index)removes by position.remove(Object o)removes by value.
When you call list.remove(1), the literal 1 is an int. Java picks remove(int index) because an exact int overload exists. It does NOT autobox 1 to Short or Integer.
How does the trap manifest?
package com.bank.util;
import java.util.ArrayList;
import java.util.List;
public class AutoboxingTrapDemo {
public static void main(String[] args) {
List<Short> list = new ArrayList<>();
for (short i = 0; i < 5; i++) {
list.add(i); // autoboxes short to Short
if (list.size() > 1) {
list.remove(i); // TRAP: i is widened to int!
// This calls remove(int index), NOT remove(Short value)
}
}
System.out.println(list);
}
}
The remove(i) call removes the element AT INDEX i, not the element WITH VALUE i. This causes IndexOutOfBoundsException when i exceeds the list size.
How do you fix it?
// CORRECT: remove by VALUE using explicit boxing
list.remove(Short.valueOf(i)); // remove the Short with value i
// CORRECT: remove by VALUE using cast
list.remove((Short) i); // cast short to Short wrapper
// SAFEST: use removeIf -- no overload ambiguity
short target = i;
list.removeIf(x -> x == target); // removes elements matching value
How do you demonstrate the difference?
List<Integer> nums = new ArrayList<>(List.of(10, 20, 30, 40));
// Remove by INDEX: removes element at position 1 (which is 20)
nums.remove(1); // [10, 30, 40]
// Remove by VALUE: removes the Integer object 30
nums.remove(Integer.valueOf(30)); // [10, 40]
What are the overload resolution rules?
| Argument Type | Method Called | Reason |
|---------------|-------------|--------|
| int literal (1) | remove(int index) | Exact match for int parameter |
| short variable | remove(int index) | short widens to int (widening > boxing) |
| Short object | remove(Object o) | Short is an Object |
| Integer.valueOf(1) | remove(Object o) | Integer is an Object |
Java's overload resolution prefers widening over boxing. A short widens to int, matching remove(int index), rather than boxing to Short to match remove(Object).
What are common mistakes?
WRONG: Assuming list.remove(i) removes by value when i is int.
WRONG: Not realizing that short widens to int before overload resolution.
What should a senior engineer know?
- This trap is specific to
ListbecauseListinheritsremove(int index)fromjava.util.ListANDremove(Object)fromjava.util.Collection. The two overloads create the ambiguity. Set.remove(Object)has only one overload, so this trap does not apply to Sets.- In production banking code, prefer
removeIf(predicate)for value-based removal. It is clear, safe, and avoids the overload trap entirely. - This is one of the top 5 Java interview trick questions alongside
==vsequals,Stringpool,finallywithreturn, andConcurrentModificationException.
Visual Overview
List<Integer> nums = [10, 20, 30, 40] nums.remove(1) ┌─────────────────────────────────────┐ │ Argument: int 1 │ │ Match: remove(int index) │ │ Action: remove element at index 1 │ │ Removed: 20 │ │ Result: [10, 30, 40] │ └─────────────────────────────────────┘ nums.remove(Integer.valueOf(30)) ┌─────────────────────────────────────┐ │ Argument: Integer 30 │ │ Match: remove(Object o) │ │ Action: remove first occurrence of 30│ │ Removed: 30 │ │ Result: [10, 40] │ └─────────────────────────────────────┘
Key Takeaway
The autoboxing trap occurs because List has two remove overloads: remove(int index) for position-based removal and remove(Object) for value-based removal. When the argument is a primitive int or short (widened to int), Java calls the index overload because overload resolution prefers widening over boxing.
To remove by value, explicitly box the argument: list.remove(Short.valueOf(i)) or use removeIf(x -> x == target) which has no overload ambiguity. The removeIf approach is the safest and most readable.
At National Bank of India, a list of short settlement codes is pruned in a loop. Calling remove(code) where code is a primitive short (widened to int) silently deletes the wrong element and corrupts the batch. Using removeIf prevents this bug.
Remember one sentence: remove(int) is by index, remove(Object) is by value, and Java picks by-index when the argument is a primitive int.
---