Suppression appropriée d'un entier d'une liste <Integer>

201

Voici un joli piège que je viens de rencontrer. Considérez une liste d'entiers:

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

Une supposition éclairée sur ce qui se passe lorsque vous exécutez list.remove(1)? Et alors list.remove(new Integer(1))? Cela peut provoquer des bugs désagréables.

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


Le principal point à considérer ici est celui mentionné par @Nikita - la correspondance exacte des paramètres a priorité sur la boxe automatique.

Yuval Adam
la source
11
A: la vraie question ici est que quelqu'un Sun en quelque sorte pensé avoir des classes wrapper (immuable) autour de primitives était intelligent et plus tard quelqu'un a pensé qu'avoir automatique (non) la boxe était encore plus intelligent ... et que les gens TENIR utilisant des API DEFAULT lamé QUAND MIEUX EXISTENT . Pour de nombreuses raisons, il existe une solution bien meilleure que la nouvelle Arraylist <Integer> . Par exemple, Trove fournit des choses avec un TIntArrayList . Plus je programme en Java (SCJP depuis 2001), moins j'utilise de classes wrapper et plus j'utilise des API bien conçues (Trove, Google, etc. me vient à l'esprit).
SyntaxT3rr0r

Réponses:

231

Java appelle toujours la méthode qui convient le mieux à votre argument. La boxe automatique et la conversion ascendante implicite ne sont effectuées que s'il n'y a pas de méthode pouvant être appelée sans casting / boxe automatique.

L'interface List spécifie deux méthodes de suppression (veuillez noter la dénomination des arguments):

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

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

alias
la source
110
Cueillir un peu: Integer.valueOf(1)est une meilleure pratique que new Integer(1). La méthode statique peut faire la mise en cache et ainsi de suite, donc vous obtiendrez de meilleures performances.
decitrig
La proposition de Peter Lawrey est meilleure et évite les créations d'objets inutiles.
assylias
@assylias: la proposition de Peter Lawrey fait exactement la même chose que la proposition de decitrig, mais de façon moins transparente.
Mark Peters
@MarkPeters Mon commentaire portait sur new Integer(1), mais je suis d'accord Integer.valueOf(1)ou (Integer) 1sont équivalents.
assylias
68

Vous pouvez utiliser le casting

list.remove((int) n);

et

list.remove((Integer) n);

Peu importe si n est un entier ou un entier, la méthode appellera toujours celle que vous attendez.

Utiliser (Integer) nou Integer.valueOf(n)est plus efficace que new Integer(n)les deux premiers peuvent utiliser le cache Integer, tandis que le dernier créera toujours un objet.

Peter Lawrey
la source
2
ce serait bien si vous pouviez expliquer pourquoi c'est le cas :) [conditions d'autoboxing ...]
Yuval Adam
En utilisant la conversion, 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 Integer . 'n' sera converti / encadré / déballé comme requis ou vous obtiendrez des erreurs de compilation si ce n'est pas le cas.
Peter Lawrey
10

Je ne connais pas la bonne façon, mais la façon dont vous avez suggéré fonctionne très bien:

list.remove(int_parameter);

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

list.remove(Integer_parameter);

supprime l'objet donné de la liste.

C'est parce que VM essaie d'abord de trouver la méthode déclarée avec exactement le même type de paramètre et essaie seulement ensuite la case automatique.

Nikita Rybak
la source
7

list.remove(4)est une correspondance exacte de list.remove(int index), il sera donc appelé. Si vous voulez appeler list.remove(Object)procédez comme suit: list.remove((Integer)4).

Petar Minchev
la source
Merci Petar, un (Integer)casting simple comme vous l'avez écrit ci-dessus semble être l'approche la plus simple pour moi.
vikingsteve
Lorsque vous utilisez votre dernière approche, il semble renvoyer un booléen. Lorsque j'essaye d'empiler plusieurs suppressions, j'obtiens l'erreur que je ne peux pas appeler remove sur un booléen.
Bram Vanroy
4

Une supposition éclairée sur ce qui se passe lorsque vous exécutez list.remove (1)? Qu'en est-il de list.remove (nouvel entier (1))?

Inutile de deviner. Le premier cas entraînera List.remove(int)un appel et l'élément en position 1sera supprimé. Le deuxième cas entraînera List.remove(Integer)un appel et l'élément dont la valeur est égale à Integer(1)sera supprimé. Dans les deux cas, le compilateur Java sélectionne la surcharge correspondante la plus proche.

Oui, il y a un risque de confusion (et de bugs) ici, mais c'est un cas d'utilisation assez rare.

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

Stephen C
la source
2

Notez que même si la machine virtuelle n'a pas fait la bonne chose, ce qu'elle fait, vous pouvez toujours garantir un comportement correct en utilisant le fait qui remove(java.lang.Object)fonctionne sur des objets arbitraires:

myList.remove(new Object() {
  @Override
  public boolean equals(Object other) {
    int k = ((Integer) other).intValue();
    return k == 1;
  }
}
user268396
la source
Cette "solution" rompt le contrat de la equalsméthode, spécifiquement (à partir de la Javadoc) "Elle est symétrique: pour toute valeur de référence non nulle x et y, x.equals (y) doit retourner vrai si et seulement si y.equals ( x) renvoie vrai. ". En tant que tel, il n'est pas garanti de fonctionner sur toutes les implémentations de List, car toute implémentation de List est autorisée à permuter le x et le y x.equals(y)à volonté, car le Javadoc de Object.equalsdit que cela devrait être valide.
Erwin Bolwidt du
1

J'ai simplement aimé suivre comme suggéré par #decitrig dans le premier commentaire de réponse acceptée.

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

Cela m'a aidé. Merci encore #decitrig pour votre commentaire. Cela peut aider quelqu'un.

Shylendra Madda
la source
0

Eh bien, voici l'astuce.

Prenons deux exemples ici:

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

  }

}

Voyons maintenant la sortie:

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]

Analysons maintenant la sortie:

  1. Lorsque 3 est supprimé de la collection, il appelle la remove()méthode de la collection qui prend Object ocomme paramètre. Par conséquent, il supprime l'objet 3. Mais dans l'objet arrayList, il est remplacé par l'index 3 et donc le 4ème élément est supprimé.

  2. Par la même logique de suppression d'objet, null est supprimé dans les deux cas dans la deuxième sortie.

Donc, pour supprimer le nombre 3qui est un objet, nous devrons explicitement passer 3 en tant queobject .

Et cela peut être fait par cast ou wrapping en utilisant la classe wrapper Integer .

Par exemple:

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