2015-05-20 13 views
19

Ho seguito due scenari: il valorecomportamento indesiderato di ArrayList rimuovere() in Java

1. int come parametro di

int intNum = 2; 
List<Integer> list = new ArrayList<Integer>(); 
list.add(1); 
list.add(2); 
list.add(3); 
list.remove(intNum); 
System.out.println(list.size()); 
// output: 2 

2. valore a lungo come parametro di

long longNum = 2; 
List<Integer> list = new ArrayList<Integer>(); 
list.add(1); 
list.add(2); 
list.add(3); 
list.remove(longNum); 
System.out.println(list.size()); 
// output: 3 

Sto passando 2 come valore in entrambi i casi, ma sto ottenendo un valore di dimensioni diverse della lista. Qual è la vera ragione di questo comportamento?

Properly removing an Integer from a List non spiega su build-in tipo di dati con lo stesso valore, ma avendo un comportamento diverso, come chiesto in precedenza

+0

Ora che si vede che elemento list.remove (2) è la rimozione dall'indice 2 e non intero 2, si dovrebbe fare un punto per cast esplicito su "int" o "Integer" o si rischia solo di confondersi. – Neil

+0

Questa potrebbe essere una bella domanda per un'intervista tecnica. – beetstra

risposta

24

Autoboxing

Il metodo list.remove è sovraccarico, e le due diverse firme sono usate per scopi diversi. Uno, list.remove(int), rimuove un elemento in base all'indice e l'altro, list.remove(Object), rimuove un elemento in base all'uguaglianza dell'oggetto. La prima situazione attiva il primo tipo e il secondo esempio (con un long longNum), attiva il secondo tipo, autoboxing della primitiva long su un oggetto java.lang.Long. Questo non è uguale ai valori java.lang.Integer (autoboxed) aggiunti all'elenco e quindi non rimuoverà nulla dall'elenco e la dimensione rimarrà la stessa.

+7

E la ragione per cui l'overload di 'Object' è scelto nel secondo caso è perché' long' non può essere convertito in 'int'. L'overload 'Object' è l'unico che può eventualmente essere applicato, – Arkadiy

+1

Anche se la prima situazione ha attivato il secondo tipo, il risultato sarebbe diverso. – xehpuk

5

Dal List.remove() documentation:

remove (int index) Rimuove il elemento nella posizione specificata in questo elenco (operazione opzionale).

remove (Object o) Rimuove la prima occorrenza dell'elemento specificato da questo elenco, se presente (operazione opzionale).

removeAll (Collection c) Rimuove da questo elenco tutti gli elementi contenuti nella raccolta specificata (operazione opzionale).

Se il secondo esempio è effettivamente lungo, non verrà rimosso (poiché utilizza il secondo metodo di rimozione).

4

L'interfaccia List contiene due metodi remove() - remove(Object) e remove(int).

L'implementazione di remove(Object) in Java 6 è la seguente:

public boolean remove(Object o) { 
if (o == null) { 
     for (int index = 0; index < size; index++) 
    if (elementData[index] == null) { 
     fastRemove(index); 
     return true; 
    } 
} else { 
    for (int index = 0; index < size; index++) 
    if (o.equals(elementData[index])) { 
     fastRemove(index); 
     return true; 
    } 
    } 
return false; 
} 

L'implementazione di remove(int) in Java 6 è:

public E remove(int index) { 
RangeCheck(index); 

modCount++; 
E oldValue = (E) elementData[index]; 

int numMoved = size - index - 1; 
if (numMoved > 0) 
    System.arraycopy(elementData, index+1, elementData, index, 
      numMoved); 
elementData[--size] = null; // Let gc do its work 

return oldValue; 
} 

Nel tuo primo esempio, si sta effettivamente chiamando il remove(int) metodo, che rimuove l'oggetto nell'indice specificato. In questo caso, hai specificato l'indice 2, che in realtà è il valore "3".

Nel vostro secondo esempio, si sta chiamando il metodo remove(Object), dal momento che non v'è un metodo remove(long) e un long non sarà convertito in un int. Basato sull'implementazione del metodo remove(Object), cerca l'uguaglianza degli oggetti. Poiché il tuo elenco contiene oggetti di tipo Integer e stai fornendo uno Long, nulla lo abbinerà.

Il seguente metodo è probabilmente un esempio migliore di quello che sta accadendo:

public static void main(String[] args) { 
    ArrayList<Integer> list; 

    System.out.println("Removing intNum"); 
    int intNum = 2; 
    list = new ArrayList<Integer>(); 
    list.add(1); 
    list.add(2); 
    list.add(3); 
    System.out.println("List = " + list); 
    list.remove(intNum); 
    System.out.println("List = " + list); 

    System.out.println("Removing longNum"); 
    long longNum = 2; 
    list = new ArrayList<Integer>(); 
    list.add(1); 
    list.add(2); 
    list.add(3); 
    System.out.println("List = " + list); 
    list.remove(longNum); 
    System.out.println("List = " + list); 
} 

L'uscita di questo codice è:

Removing intNum 
List = [1, 2, 3] 
List = [1, 2] 
Removing longNum 
List = [1, 2, 3] 
List = [1, 2, 3] 
2

Quando si eseguiva il numero list.remove(intNum);, stava eseguendo la firma remove(int index); della classe List, che rimuove l'elemento dell'indice specificato.

Tuttavia quando hai fatto list.remove(longNum); (considerando che intendevi longNum essere long), ha eseguito la firma boolean remove(Object o); di List classe, che controlla se l'oggetto è presente nella lista, e se sì, lo rimuove.

Poiché l'elenco è un elenco Integer, non è stato possibile trovare l'oggetto e non ha rimosso nulla ed è per questo che il secondo risultato è 3, non viene eliminato nulla.

2

Calling List.remove() con un argomento int corrisponderà la firma remove(int index) e la voce sull'indice list[2] sarà rimosso a prescindere se la lista ha un elemento Integer con valore pari a 2.

Calling List.remove () con un argomento lungo farà sì che il compilatore esegua il box automatico in un oggetto Long e corrisponda alla firma remove(Object o). L'elenco verrà iterato, chiedendo se o.equals (ogni elemento) e come menzionato in precedenza, Long non è uguale a Intero.

2

List in collezione ha due metodi di overload 1. E remove pubblico (int index) 2. public boolean remove (Object o)

nel tuo caso secondo metodo viene chiamato. dal momento che stai passando a lungo (getto implicito alla classe con wrapper lungo, quindi classe Object).

Ora List.remove (longNum) rimuove l'elemento con il più basso indice i tale che (longNum == null ottenere (i) == null:? LongNum.equals (ottenere (i))) (se tale un elemento esiste).

Ora nel Tuo caso quando il contatore è arrivato in seconda posizione (cioè i = 1). longNum.equals (get (1)) restituisce false perché get (1) restituisce l'oggetto di java.lang.Integer con valore = 2 e longNum è un'istanza di java.lang.Long con valore = 2. Quindi il metodo equals restituisce false quindi non cancella l'elemento.

È possibile controllare il tipo di valore per la sua classe di nome

System.out.println(" Element Value :"+list.get(1)); 
    System.out.println(" Element Class :"+list.get(1).getClass());