Richtiges Entfernen einer Ganzzahl aus einer Liste <Integer>

201

Hier ist eine schöne Falle, auf die ich gerade gestoßen bin. Betrachten Sie eine Liste von ganzen Zahlen:

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

Irgendwelche fundierten Vermutungen darüber, was passiert, wenn Sie ausführen list.remove(1)? Was ist mit list.remove(new Integer(1))? Dies kann einige böse Fehler verursachen.

Was ist der richtige Weg, um zu unterscheiden remove(int index), was ein Element aus einem bestimmten Index entfernt und remove(Object o)was ein Element durch Bezugnahme entfernt, wenn es sich um Listen von ganzen Zahlen handelt?


Der wichtigste Punkt, der hier berücksichtigt werden muss, ist der erwähnte @Nikita - die genaue Parameterübereinstimmung hat Vorrang vor dem automatischen Boxen.

Yuval Adam
quelle
11
A: Das eigentliche Problem hier ist, dass jemand bei Sun irgendwie dachte, (unveränderliche) Wrapper-Klassen um Primitive zu haben, und später dachte jemand, dass Auto- (Un-) Boxen noch schlauer sei ... UND DASS MENSCHEN LAME DEFAULT-APIs VERWENDEN WENN BESSERE BESTEHEN . Für viele Zwecke gibt es eine viel bessere Lösung als die neue Arraylist <Integer> . Zum Beispiel stellt Trove den Dingen eine TIntArrayList zur Verfügung . Je mehr ich in Java programmiere (SCJP seit 2001), desto weniger Wrapper-Klassen verwende ich und desto häufiger verwende ich gut gestaltete APIs (Trove, Google usw.).
SyntaxT3rr0r

Antworten:

230

Java ruft immer die Methode auf, die am besten zu Ihrem Argument passt. Auto-Boxing und implizites Upcasting werden nur durchgeführt, wenn es keine Methode gibt, die ohne Casting / Auto-Boxing aufgerufen werden kann.

Die List-Schnittstelle gibt zwei Entfernungsmethoden an (bitte beachten Sie die Benennung der Argumente):

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

Dies bedeutet, dass list.remove(1)das Objekt an Position 1 remove(new Integer(1))entfernt und das erste Vorkommen des angegebenen Elements aus dieser Liste entfernt wird.

aka
quelle
110
Picking a nit: Integer.valueOf(1)ist besser als new Integer(1). Die statische Methode kann Caching und dergleichen ausführen, um eine bessere Leistung zu erzielen.
decitrig
Peter Lawreys Vorschlag ist besser und vermeidet unnötige Objekterstellungen.
Assylias
@assylias: Peter Lawreys Vorschlag macht genau das Gleiche wie der Vorschlag von decitrig, nur weniger transparent.
Mark Peters
@MarkPeters Mein Kommentar war ungefähr new Integer(1), aber ich stimme dem zu Integer.valueOf(1)oder bin (Integer) 1gleichwertig.
Assylias
68

Sie können Casting verwenden

list.remove((int) n);

und

list.remove((Integer) n);

Es spielt keine Rolle, ob n ein int oder eine Ganzzahl ist, die Methode ruft immer die erwartete auf.

Die Verwendung von (Integer) noder Integer.valueOf(n)ist effizienter als new Integer(n)die ersten beiden, die den Integer-Cache verwenden können, während die letztere immer ein Objekt erstellt.

Peter Lawrey
quelle
2
Es wäre schön, wenn Sie erklären könnten, warum dies der Fall ist :) [Autoboxing-Bedingungen ...]
Yuval Adam
Durch die Verwendung von Casting stellen Sie sicher, dass der Compiler den erwarteten Typ sieht. Im ersten Fall kann '(int) n' nur vom Typ int sein, im zweiten Fall kann '(Integer) n' nur vom Typ Integer sein . 'n' wird nach Bedarf konvertiert / boxed / unboxed oder Sie erhalten einen Compilerfehler, wenn dies nicht möglich ist.
Peter Lawrey
10

Ich weiß nicht, wie man es richtig macht, aber die von Ihnen vorgeschlagene Art funktioniert einwandfrei:

list.remove(int_parameter);

entfernt das Element an der angegebenen Position und

list.remove(Integer_parameter);

Entfernt ein bestimmtes Objekt aus der Liste.

Dies liegt daran, dass VM zunächst versucht, eine Methode zu finden, die mit genau demselben Parametertyp deklariert wurde , und erst dann versucht, eine Autobox durchzuführen.

Nikita Rybak
quelle
7

list.remove(4)ist eine genaue Übereinstimmung von list.remove(int index), so wird es aufgerufen. Wenn Sie anrufen möchten, list.remove(Object)gehen Sie wie folgt vor : list.remove((Integer)4).

Petar Minchev
quelle
Danke Petar, eine einfache (Integer)Besetzung, wie Sie sie oben geschrieben haben, scheint für mich der einfachste Ansatz zu sein.
Wikingersteve
Wenn Sie Ihren letzten Ansatz verwenden, scheint er einen Booleschen Wert zurückzugeben. Beim Versuch, mehrere Entfernungen zu stapeln, wird die Fehlermeldung angezeigt, dass ich remove nicht für einen Booleschen Wert aufrufen kann.
Bram Vanroy
4

Irgendwelche fundierten Vermutungen darüber, was passiert, wenn Sie list.remove (1) ausführen? Was ist mit list.remove (neue Ganzzahl (1))?

Es besteht kein Grund zu raten. Der erste Fall führt dazu, List.remove(int)dass aufgerufen wird und das Element an der Position 1entfernt wird. Der zweite Fall führt dazu, List.remove(Integer)dass aufgerufen wird und das Element, dessen Wert gleich Integer(1)ist, entfernt wird. In beiden Fällen wählt der Java-Compiler die am besten passende Überladung aus.

Ja, hier besteht die Gefahr von Verwirrung (und Fehlern), aber es ist ein ziemlich ungewöhnlicher Anwendungsfall.

Als die beiden List.removeMethoden in Java 1.2 definiert wurden, waren die Überladungen nicht mehrdeutig. Das Problem trat erst mit der Einführung von Generika und Autoboxing in Java 1.5 auf. Im Nachhinein wäre es besser gewesen, wenn eine der Entfernungsmethoden einen anderen Namen erhalten hätte. Aber jetzt ist es zu spät.

Stephen C.
quelle
2

Beachten Sie, dass Sie auch dann, wenn die VM nicht das Richtige getan hat, das richtige Verhalten sicherstellen können, indem Sie die Tatsache verwenden, dass remove(java.lang.Object)mit beliebigen Objekten gearbeitet wird:

myList.remove(new Object() {
  @Override
  public boolean equals(Object other) {
    int k = ((Integer) other).intValue();
    return k == 1;
  }
}
user268396
quelle
Diese "Lösung" bricht den Vertrag der equalsMethode, insbesondere (aus dem Javadoc). "Sie ist symmetrisch: Für alle Nicht-Null-Referenzwerte x und y sollte x.equals (y) genau dann true zurückgeben, wenn y.equals ( x) gibt true zurück. ". Als solches ist es nicht garantiert, dass es bei allen Implementierungen von funktioniert List, da jede Implementierung von List das x und das y nach x.equals(y)Belieben austauschen darf, da der Javadoc von Object.equalssagt, dass dies gültig sein sollte.
Erwin Bolwidt
1

Ich habe einfach gerne gefolgt, wie von #decitrig in der akzeptierten Antwort als erster Kommentar vorgeschlagen.

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

Das hat mir geholfen. Nochmals vielen Dank #decitrig für Ihren Kommentar. Es kann für jemanden helfen.

Shylendra Madda
quelle
0

Nun, hier ist der Trick.

Nehmen wir hier zwei Beispiele:

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

  }

}

Schauen wir uns nun die Ausgabe an:

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]

Lassen Sie uns nun die Ausgabe analysieren:

  1. Wenn 3 aus der Sammlung entfernt wird, wird die remove()Methode der Sammlung aufgerufen, die Object oals Parameter verwendet wird. Daher wird das Objekt entfernt 3. Im ArrayList-Objekt wird es jedoch durch Index 3 überschrieben, und daher wird das 4. Element entfernt.

  2. Nach der gleichen Logik der Objektentfernung wird in beiden Fällen in der zweiten Ausgabe null entfernt.

Um die Zahl zu entfernen, die 3ein Objekt ist, müssen wir explizit 3 als übergeben object.

Dies kann durch Casting oder Wrapping mit der Wrapper-Klasse erfolgen Integer.

Z.B:

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