222 votes

Supprimer correctement un Integer d'une List<Integer>

Voici un bel écueil que je viens de rencontrer. Considérons une liste d'entiers :

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

Quelle est votre opinion sur ce qui se passe lorsque vous exécutez list.remove(1) ? Qu'en est-il list.remove(new Integer(1)) ? Cela peut provoquer des bogues désagréables.

Quelle est la bonne façon de faire la différence entre remove(int index) qui supprime un élément à partir d'un indice donné et remove(Object o) qui supprime un élément par référence, lorsqu'il s'agit de listes d'entiers ?


Le point principal à considérer ici est celui @Nikita a mentionné - la correspondance exacte des paramètres a la priorité sur l'auto-boxing.

12 votes

A : le vrai problème ici est que quelqu'un chez Sun a pensé qu'avoir des classes enveloppantes (immuables) autour des primitives était intelligent et plus tard quelqu'un a pensé qu'avoir l'auto-(un)boxing était encore plus intelligent... ET QUE LES GENS CONTINUENT À UTILISER DE MAUVAISES API PAR DÉFAUT QUAND IL EN EXISTE DE MEILLEURES . Pour de nombreuses raisons, il existe bien meilleur solution que nouveau Arraylist<Integer> . Par exemple, Trove fournit des éléments tels que TIntArrayList . Plus je programme en Java (SCJP depuis 2001), moins j'utilise de classes enveloppantes et plus j'utilise des API bien conçues (Trove, Google, etc. me viennent à l'esprit).

246voto

aka Points 665

Java appelle toujours la méthode qui convient le mieux à votre argument. L'auto-boxing et l'upcasting implicite ne sont effectués que s'il n'existe pas de méthode pouvant être appelée sans casting / auto-boxing.

L'interface List spécifie deux méthodes de suppression (veuillez noter le nom des arguments) :

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

Cela signifie que list.remove(1) retire l'objet à la position 1 et remove(new Integer(1)) supprime de cette liste la première occurrence de l'élément spécifié.

123 votes

Une pinaillerie : Integer.valueOf(1) est une meilleure pratique que new Integer(1) . La méthode statique peut être mise en cache, ce qui permet d'obtenir de meilleures performances.

0 votes

La proposition de Peter Lawrey est meilleure et évite les créations d'objets inutiles.

0 votes

@assylias : La proposition de Peter Lawrey fait exactement la même chose que celle de decitrig, mais de manière moins transparente.

73voto

Peter Lawrey Points 229686

Vous pouvez utiliser le casting

list.remove((int) n);

y

list.remove((Integer) n);

Peu importe que n soit un int ou un Integer, la méthode appellera toujours celle que vous attendez.

Utilisation (Integer) n o Integer.valueOf(n) est plus efficace que new Integer(n) car les deux premiers peuvent utiliser le cache Integer, alors que le dernier créera toujours un objet.

2 votes

Il serait bon que vous expliquiez pourquoi c'est le cas :) [conditions d'autoboxage...]

0 votes

En utilisant le casting, vous vous assurez que le compilateur voit le type que vous attendez. Dans le premier cas, '(int) n' ne peut être que de type int dans le second cas, '(Integer) n' ne peut être que de type Entier . 'n' sera converti/encadré/décadré comme il se doit ou vous obtiendrez une erreur du compilateur si ce n'est pas le cas.

10voto

Nikita Rybak Points 36641

Je ne sais pas s'il s'agit d'une méthode "appropriée", mais la méthode que vous avez suggérée fonctionne très bien :

list.remove(int_parameter);

supprime l'élément à la position donnée et

list.remove(Integer_parameter);

supprime l'objet donné de la liste.

C'est parce que VM tente d'abord de trouver les méthodes déclarées avec la mention exactement le même type de paramètre et ne tente l'autoboxage qu'ensuite.

7voto

Petar Minchev Points 24864

list.remove(4) est une correspondance exacte de list.remove(int index) Il s'appellera ainsi. Si vous souhaitez appeler list.remove(Object) faire ce qui suit : list.remove((Integer)4) .

0 votes

Merci Petar, un simple (Integer) comme vous l'avez écrit ci-dessus semble être l'approche la plus facile pour moi.

0 votes

En utilisant votre dernière approche, il semble renvoyer un booléen. Lorsque j'essaie d'empiler plusieurs suppressions, j'obtiens l'erreur suivante : je ne peux pas appeler la suppression sur un booléen.

5voto

Stephen C Points 255558

Que se passe-t-il lorsque l'on exécute list.remove(1) ? Qu'en est-il de list.remove(new Integer(1)) ?

Il n'est pas nécessaire de deviner. Dans le premier cas, il en résultera List.remove(int) étant appelé, et l'élément à la position 1 sera supprimée. Dans le second cas, il y aura List.remove(Integer) est appelé, et l'élément dont la valeur est égale à Integer(1) sera supprimée. Dans les deux cas, le compilateur Java sélectionne la surcharge la plus proche.

Oui, il existe un risque de confusion (et de bogues), mais il s'agit d'un cas d'utilisation peu courant.

Lorsque les deux List.remove ont été définies dans Java 1.2, les surcharges n'étaient pas ambiguës. Le problème n'est apparu qu'avec l'introduction des génériques et de l'autoboxing dans Java 1.5. Rétrospectivement, il aurait été préférable de donner un autre nom à l'une des méthodes de suppression. Mais il est trop tard maintenant.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X