Как метод remove в коллекциях Java определяет, удалять элемент по индексу или по объекту
Что проверяют
Понимаете ли вы, что выбор между remove(int) и remove(Object) делает компилятор по статическому типу аргумента, а не среда выполнения. Следом почти всегда дают List<Integer> и спрашивают, что удалит list.remove(1).
Как отвечать
У List два перегруженных метода:
E remove(int index): объявлен вList, удаляет по позиции и возвращает удалённый элемент, при неверном индексе бросаетIndexOutOfBoundsException;boolean remove(Object o): пришёл изCollection, удаляет первое вхождение, найденное черезequals, и возвращаетtrue, если что-то удалил.
У Set и остальных коллекций есть только второй вариант, вопрос выбора возникает именно у списков.
Перегрузка разрешается при компиляции в три фазы. Сначала компилятор ищет подходящий метод без упаковки и распаковки, только с расширением типов. Если нашёл, дальше не идёт. Отсюда поведение:
list.remove(1): литералintточно подходит кremove(int), удаляется элемент с индексом 1;list.remove(Integer.valueOf(1))илиlist.remove((Object) 1): аргумент ссылочный, подходитremove(Object), удаляется значение 1;- переменная типа
Integer: тожеremove(Object), распаковка не нужна, потому чтоIntegerи так подтипObject; shortилиchar: расширяются доint, значит удаление по индексу;longвList<Long>: сузитьlongдоintнельзя, поэтому срабатывает упаковка вLongи удаление по значению.
Для удаления по условию есть removeIf(Predicate), а во время обхода удалять нужно через Iterator.remove().
Где спотыкаются
Говорят, что метод выбирается во время выполнения по типу аргумента. Динамически выбирается только реализация у конкретного класса, а сама сигнатура зашита в байткод при компиляции.
Удаляют числа из List<Integer> через remove(x) в цикле и получают либо IndexOutOfBoundsException, либо тихо удалённый не тот элемент.
Вызывают remove на списке из List.of() или Arrays.asList() и удивляются UnsupportedOperationException: первый неизменяемый, у второго фиксированный размер.