Правильное удаление целого числа из списка <Integer>


201

Вот хорошая ловушка, с которой я только что столкнулся. Рассмотрим список целых чисел:

List<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(6);
list.add(7);
list.add(1);

Любое обоснованное предположение о том, что происходит, когда вы выполняете list.remove(1) ? Как насчет list.remove(new Integer(1))? Это может вызвать некоторые неприятные ошибки.

Каков правильный способ различения remove(int index), который удаляет элемент из данного индекса иremove(Object o) который удаляет элемент по ссылке при работе со списками целых чисел?


Главное, на что следует обратить внимание, это упомянутое @Nikita - точное сопоставление параметров имеет приоритет над автобоксом .


11
A: реальная проблема здесь в том, что кто-то в Sun почему-то думал, что иметь (неизменяемые) классы-обертки вокруг примитивов было умно, а позже кто-то подумал, что иметь автоматический (не) бокс еще более умно ... И ЭТО ЛЮДИ ИСПОЛЬЗУЮТ API-интерфейсы LAME DEFAULT КОГДА ЛУЧШЕ ЕСТЬ . Для многих целей есть лучшее решение, чем новый Arraylist <Integer> . Например, Trove предоставляет вещи в виде TIntArrayList . Чем больше я программирую на Java (SCJP с 2001 года), тем меньше я использую классы-обертки и тем больше я использую хорошо разработанные API (Trove, Google и т. Д.).
СинтаксисT3rr0r

Ответы:


231

Java всегда вызывает метод, который лучше всего подходит для вашего аргумента. Автобокс и неявное апкастинг выполняются только в том случае, если нет метода, который можно вызвать без приведения / автобокса.

Интерфейс List определяет два метода удаления (обратите внимание на именование аргументов):

  • remove(Object o)
  • remove(int index)

Это означает, что list.remove(1)удаляет объект в позиции 1 и remove(new Integer(1))удаляет первое вхождение указанного элемента из этого списка.


110
Сбор гниды: Integer.valueOf(1)это лучшая практика, чем new Integer(1). Статический метод может выполнять кэширование и тому подобное, поэтому вы получите лучшую производительность.
Decitrig

Предложение Питера Лоури лучше и позволяет избежать ненужных объектов.
assylias

@assylias: предложение Питера Лори делает то же самое, что и предложение decitrig, но менее прозрачно.
Марк Питерс

@MarkPeters Мой комментарий был о new Integer(1), но я согласен, что Integer.valueOf(1)или (Integer) 1эквивалентны.
assylias

68

Вы можете использовать кастинг

list.remove((int) n);

и

list.remove((Integer) n);

Не имеет значения, является ли n целым или целым, метод всегда будет вызывать тот, который вы ожидаете.

Использование (Integer) nили Integer.valueOf(n)более эффективно, чем new Integer(n)в качестве первых двух, может использовать кэш Integer, тогда как последнее всегда будет создавать объект.


2
было бы хорошо, если бы вы могли объяснить, почему это так :) [условия автобокса ...]
Ювал Адам

Используя приведение, вы гарантируете, что компилятор увидит ожидаемый вами тип. В первом случае '(int) n' может иметь тип только int, во втором случае '(Integer) n' может быть только тип Integer . 'n' будет конвертировано / упаковано / распаковано по мере необходимости, иначе вы получите ошибки компилятора, если это не удастся.
Питер Лори

10

Я не знаю о «правильном» способе, но предложенный вами способ прекрасно работает:

list.remove(int_parameter);

удаляет элемент в данной позиции и

list.remove(Integer_parameter);

удаляет данный объект из списка.

Это потому, что VM сначала пытается найти метод, объявленный с точно таким же типом параметра, и только потом пытается выполнить автоматическую коробку.


7

list.remove(4)это точное совпадение list.remove(int index), поэтому оно будет называться. Если вы хотите , чтобы вызов list.remove(Object)сделать следующее: list.remove((Integer)4).


Спасибо, Петар, простой (Integer)актерский состав, как ты написал выше, кажется мне самым легким подходом.
Викингстеве

При использовании вашего последнего подхода, кажется, возвращает логическое значение. При попытке сложить несколько удалений я получаю сообщение об ошибке, которое не могу вызвать remove для логического значения.
Брэм Ванрой

4

Любое обоснованное предположение о том, что происходит, когда вы выполняете list.remove (1)? Что насчет list.remove (new Integer (1))?

Там нет необходимости угадывать. Первый случай приведет к List.remove(int)вызову, и элемент в позиции 1будет удален. Второй случай приведет к List.remove(Integer)вызову, и элемент, значение которого равно, Integer(1)будет удален. В обоих случаях компилятор Java выбирает наиболее подходящую перегрузку.

Да, здесь есть вероятность путаницы (и ошибок), но это довольно необычный вариант использования.

Когда эти два List.removeметода были определены в Java 1.2, перегрузки не были неоднозначными. Проблема возникла только с введением обобщений и автобокс в Java 1.5. В ретроспективе было бы лучше, если бы одному из методов удаления было присвоено другое имя. Но сейчас уже слишком поздно.


2

Обратите внимание, что даже если виртуальная машина не сделала правильно, что она делает, вы все равно могли бы обеспечить правильное поведение, используя тот факт, что remove(java.lang.Object)работает с произвольными объектами:

myList.remove(new Object() {
  @Override
  public boolean equals(Object other) {
    int k = ((Integer) other).intValue();
    return k == 1;
  }
}

Это «решение» нарушает контракт equalsметода, в частности (из Javadoc). «Это симметрично: для любых ненулевых ссылочных значений x и y x.equals (y) должен возвращать true тогда и только тогда, когда y.equals ( х) возвращает истину. Как таковой, он не гарантированно работает на всех реализациях List, потому что любая реализация List может менять местами x и y x.equals(y)по желанию, поскольку Javadoc Object.equalsговорит, что это должно быть допустимо.
Эрвин Болвидт

1

Просто мне понравилось следовать предложению #decitrig в принятом ответе.

list.remove(Integer.valueOf(intereger_parameter));

Это помогло мне. Еще раз спасибо #decitrig за ваш комментарий. Это может помочь для кого-то.


0

Ну, вот в чем фокус.

Давайте возьмем два примера здесь:

public class ArrayListExample {

public static void main(String[] args) {
    Collection<Integer> collection = new ArrayList<>();
    List<Integer> arrayList = new ArrayList<>();

    collection.add(1);
    collection.add(2);
    collection.add(3);
    collection.add(null);
    collection.add(4);
    collection.add(null);
    System.out.println("Collection" + collection);

    arrayList.add(1);
    arrayList.add(2);
    arrayList.add(3);
    arrayList.add(null);
    arrayList.add(4);
    arrayList.add(null);
    System.out.println("ArrayList" + arrayList);

    collection.remove(3);
    arrayList.remove(3);
    System.out.println("");
    System.out.println("After Removal of '3' :");
    System.out.println("Collection" + collection);
    System.out.println("ArrayList" + arrayList);

    collection.remove(null);
    arrayList.remove(null);
    System.out.println("");
    System.out.println("After Removal of 'null': ");
    System.out.println("Collection" + collection);
    System.out.println("ArrayList" + arrayList);

  }

}

Теперь давайте посмотрим на результат:

Collection[1, 2, 3, null, 4, null]
ArrayList[1, 2, 3, null, 4, null]

After Removal of '3' :
Collection[1, 2, null, 4, null]
ArrayList[1, 2, 3, 4, null]

After Removal of 'null': 
Collection[1, 2, 4, null]
ArrayList[1, 2, 3, 4]

Теперь давайте проанализируем вывод:

  1. Когда 3 удаляется из коллекции, он вызывает remove()метод коллекции, который принимает в Object oкачестве параметра. Следовательно это удаляет объект 3. Но в объекте arrayList он переопределяется индексом 3 и, следовательно, 4-й элемент удаляется.

  2. По той же логике удаления объекта null удаляется в обоих случаях во втором выводе.

Таким образом, чтобы удалить число, 3являющееся объектом, нам явно нужно будет передать 3 как object.

И это может быть сделано путем приведения или обтекания с использованием класса обертки Integer.

Например:

Integer removeIndex = Integer.valueOf("3");
collection.remove(removeIndex);
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.