Eliminar correctamente un número entero de una lista <Intero>

201

Aquí hay una buena trampa que acabo de encontrar. Considere una lista de enteros:

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

¿Alguna conjetura sobre lo que sucede cuando ejecutas list.remove(1)? ¿Qué hay de list.remove(new Integer(1))? Esto puede causar algunos errores desagradables.

¿Cuál es la forma correcta de diferenciar entre remove(int index), que elimina un elemento de un índice dado y remove(Object o), que elimina un elemento por referencia, cuando se trata de listas de enteros?


El punto principal a considerar aquí es el mencionado por @Nikita : la coincidencia exacta de parámetros tiene prioridad sobre el auto-boxeo.

Yuval Adam
fuente
11
R: el verdadero problema aquí es que alguien en Sun de alguna manera pensó que tener clases de envoltura (inmutables) alrededor de primitivas era inteligente y más tarde alguien pensó que tener auto (des) boxeo era aún más inteligente ... Y QUE LA GENTE SIGUE UTILIZANDO API DEMASIADO POR DEFECTO CUANDO MEJORES EXISTEN . Para muchos propósitos, hay una solución mucho mejor que la nueva Arraylist <Integer> . Por ejemplo, Trove proporciona cosas a TIntArrayList . Cuanto más programo en Java (SCJP desde 2001), menos uso las clases de contenedor y más uso de API bien diseñadas (Trove, Google, etc., me viene a la mente).
SyntaxT3rr0r

Respuestas:

231

Java siempre llama al método que mejor se adapte a su argumento. El boxeo automático y la conversión implícita solo se realizan si no hay un método al que se pueda llamar sin conversión / boxing automático.

La interfaz de Lista especifica dos métodos de eliminación (tenga en cuenta la denominación de los argumentos):

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

Eso significa que list.remove(1)elimina el objeto en la posición 1 y remove(new Integer(1))elimina la primera aparición del elemento especificado de esta lista.

alias
fuente
110
Escoger una liendre: Integer.valueOf(1)es mejor práctica que new Integer(1). El método estático puede hacer almacenamiento en caché y demás, por lo que obtendrá un mejor rendimiento.
decitrig
La propuesta de Peter Lawrey es mejor y evita creaciones innecesarias de objetos.
Assylias
@assylias: la propuesta de Peter Lawrey hace exactamente lo mismo que la propuesta de decitrig, solo que de manera menos transparente.
Mark Peters
@MarkPeters Mi comentario fue sobre new Integer(1), pero estoy de acuerdo Integer.valueOf(1)o (Integer) 1equivalente.
Assylias
68

Puedes usar casting

list.remove((int) n);

y

list.remove((Integer) n);

No importa si n es int o Integer, el método siempre llamará al que espera.

Usar (Integer) no Integer.valueOf(n)es más eficiente que new Integer(n)los dos primeros puede usar el caché Integer, mientras que el segundo siempre creará un objeto.

Peter Lawrey
fuente
2
sería bueno si pudieras explicar por qué ese es el caso :) [condiciones de autoboxing ...]
Yuval Adam
Al utilizar la conversión, se asegura de que el compilador vea el tipo que espera. En el primer caso, '(int) n' solo puede ser de tipo int en el segundo caso '(Integer) n' solo puede ser de tipo Integer . 'n' se convertirá / encajonará / desempaquetará según sea necesario u obtendrá un error de compilación si no puede.
Peter Lawrey
10

No sé sobre la forma 'adecuada', pero la forma en que sugirió funciona bien:

list.remove(int_parameter);

elimina el elemento en la posición dada y

list.remove(Integer_parameter);

elimina el objeto dado de la lista.

Esto se debe a que VM al principio intenta encontrar el método declarado con exactamente el mismo tipo de parámetro y solo luego intenta el autoboxing.

Nikita Rybak
fuente
7

list.remove(4)es una coincidencia exacta de list.remove(int index), por lo que se llamará. Si desea llamar list.remove(Object)hacer lo siguiente: list.remove((Integer)4).

Petar Minchev
fuente
Gracias Petar, un (Integer)elenco simple como el que escribiste anteriormente parece ser el enfoque más fácil para mí.
vikingsteve
Cuando utiliza su último enfoque, parece devolver un valor booleano. Al intentar apilar múltiples eliminaciones, aparece el error que no puedo llamar eliminar en un booleano.
Bram Vanroy
4

¿Alguna suposición educada sobre lo que sucede cuando ejecuta list.remove (1)? ¿Qué pasa con list.remove (new Integer (1))?

No hay necesidad de adivinar. Se llamará al primer caso List.remove(int)y 1se eliminará el elemento en la posición . El segundo caso resultará en List.remove(Integer)ser llamado, y el elemento cuyo valor es igual a Integer(1)será eliminado. En ambos casos, el compilador de Java selecciona la sobrecarga coincidente más cercana.

Sí, existe la posibilidad de confusión (y errores) aquí, pero es un caso de uso bastante poco común.

Cuando los dos List.removemétodos se definieron en Java 1.2, las sobrecargas no fueron ambiguas. El problema solo surgió con la introducción de genéricos y autoboxing en Java 1.5. En retrospectiva, hubiera sido mejor si uno de los métodos de eliminación hubiera recibido un nombre diferente. Pero ya es demasiado tarde.

Stephen C
fuente
2

Tenga en cuenta que incluso si la máquina virtual no hizo lo correcto, lo que hace, aún podría garantizar un comportamiento adecuado utilizando el hecho de que remove(java.lang.Object)opera en objetos arbitrarios:

myList.remove(new Object() {
  @Override
  public boolean equals(Object other) {
    int k = ((Integer) other).intValue();
    return k == 1;
  }
}
usuario268396
fuente
Esta "solución" rompe el contrato del equalsmétodo, específicamente (del Javadoc) "Es simétrico: para cualquier valor de referencia no nulo x e y, x.equals (y) debe devolver verdadero si y solo si y.equals ( x) devuelve verdadero ". Como tal, no se garantiza que funcione en todas las implementaciones de List, porque cualquier implementación de Lista puede intercambiar la x y la y x.equals(y)a voluntad, ya que el Javadoc de Object.equalsdice que esto debería ser válido.
Erwin Bolwidt
1

Simplemente me gustó seguir como lo sugirió #decitrig en el primer comentario de respuesta aceptada.

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

Esto me ayudo. Gracias de nuevo #decitrig por tu comentario. Puede ayudar a alguien.

Shylendra Madda
fuente
0

Pues aquí está el truco.

Tomemos dos ejemplos aquí:

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);

  }

}

Ahora echemos un vistazo a la salida:

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]

Ahora analicemos el resultado:

  1. Cuando se elimina 3 de la colección, llama al remove()método de la colección que toma Object ocomo parámetro. Por lo tanto, elimina el objeto 3. Pero en el objeto arrayList está anulado por el índice 3 y, por lo tanto, se elimina el cuarto elemento.

  2. Por la misma lógica de eliminación de objetos, se elimina nulo en ambos casos en la segunda salida.

Entonces, para eliminar el número 3que es un objeto, explícitamente tendremos que pasar 3 como an object.

Y eso se puede hacer fundiendo o ajustando usando la clase wrapper Integer.

P.ej:

Integer removeIndex = Integer.valueOf("3");
collection.remove(removeIndex);
Pritam Banerjee
fuente