Oto niezła pułapka, którą właśnie spotkałem. Rozważ listę liczb całkowitych:
List<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(6);
list.add(7);
list.add(1);
Jakieś wykształcone zgadywanie, co się stanie, kiedy wykonasz egzekucję list.remove(1)
? Co list.remove(new Integer(1))
? Może to powodować paskudne błędy.
Jaki jest właściwy sposób na rozróżnienie remove(int index)
, który usuwa element z danego indeksu, a remove(Object o)
który usuwa element przez odniesienie, gdy mamy do czynienia z listami liczb całkowitych?
Głównym punktem do rozważenia jest wspomniany @Nikita - dokładne dopasowanie parametrów ma pierwszeństwo przed automatycznym boksem.
java
collections
overloading
Yuval Adam
źródło
źródło
Odpowiedzi:
Java zawsze wywołuje metodę, która najlepiej odpowiada twojemu argumentowi. Automatyczne boksowanie i niejawne upcasting jest wykonywane tylko wtedy, gdy nie ma metody, którą można by wywołać bez rzutowania / auto boksowania.
Interfejs listy określa dwie metody usuwania (zwróć uwagę na nazewnictwo argumentów):
remove(Object o)
remove(int index)
Oznacza to, że
list.remove(1)
usuwa obiekt z pozycji 1 iremove(new Integer(1))
usuwa pierwsze wystąpienie określonego elementu z tej listy.źródło
Integer.valueOf(1)
jest lepszą praktyką niżnew Integer(1)
. Metoda statyczna może buforować i tym podobne, dzięki czemu uzyskasz lepszą wydajność.new Integer(1)
, ale zgadzam się z tymInteger.valueOf(1)
lub(Integer) 1
są równoważne.Możesz użyć castingu
i
Nie ma znaczenia, czy n jest liczbą całkowitą czy całkowitą, metoda zawsze wywoła tę, której się spodziewasz.
Użycie
(Integer) n
lubInteger.valueOf(n)
jest bardziej wydajne niżnew Integer(n)
w przypadku dwóch pierwszych, które mogą korzystać z pamięci podręcznej Integer, a później zawsze utworzy obiekt.źródło
Nie wiem o „właściwym” sposobie, ale sposób, który zasugerowałeś, działa dobrze:
usuwa element w danej pozycji i
usuwa dany obiekt z listy.
To dlatego, że VM przy pierwszej próbie znalezienia metody zadeklarowanej z dokładnie tym samym typem parametru, a dopiero potem próbuje autoboxowania.
źródło
list.remove(4)
jest dokładnym dopasowaniemlist.remove(int index)
, więc zostanie wywołane. Jeśli chcesz zadzwonićlist.remove(Object)
, wykonaj następujące czynności:list.remove((Integer)4)
.źródło
(Integer)
obsada, jak napisałeś powyżej, wydaje mi się najłatwiejszym podejściem.Nie trzeba zgadywać. Pierwszy przypadek spowoduje
List.remove(int)
wywołanie, a element w pozycji1
zostanie usunięty. Drugi przypadek spowodujeList.remove(Integer)
wywołanie, a element, którego wartość jest równa,Integer(1)
zostanie usunięty. W obu przypadkach kompilator Java wybiera najbliższe pasujące przeciążenie.Tak, istnieje tu możliwość zamieszania (i błędów), ale jest to dość rzadki przypadek użycia.
Kiedy dwie
List.remove
metody zostały zdefiniowane w Javie 1.2, przeciążenia nie były niejednoznaczne. Problem pojawił się dopiero wraz z wprowadzeniem generics i autoboxing w Javie 1.5. Z perspektywy czasu byłoby lepiej, gdyby jedna z metod usuwania otrzymała inną nazwę. Ale teraz jest już za późno.źródło
Zauważ, że nawet jeśli maszyna wirtualna nie postąpiła właściwie, co robi, nadal możesz zapewnić prawidłowe zachowanie, wykorzystując fakt, że
remove(java.lang.Object)
działa ona na dowolnych obiektach:źródło
equals
metody, a konkretnie (z Javadoc) „Jest symetryczne: dla dowolnych wartości zerowych x i y x.equals (y) powinno zwracać prawdę wtedy i tylko wtedy, gdy y.equals ( x) zwraca true. ”. W związku z tym nie ma gwarancji, że będzie działać na wszystkich implementacjachList
, ponieważ dowolna implementacja List może zamienić x i yx.equals(y)
do woli, ponieważ JavadocObject.equals
mówi, że powinno to być poprawne.Po prostu lubiłem podążać za sugestią #decitrig w zaakceptowanej odpowiedzi na pierwszy komentarz.
To mi pomogło. Jeszcze raz dziękuję #decitrig za komentarz. Może to komuś pomóc.
źródło
Cóż, oto sztuczka.
Weźmy dwa przykłady tutaj:
Teraz spójrzmy na wynik:
Teraz przeanalizujmy dane wyjściowe:
Usunięcie 3 z kolekcji wywołuje
remove()
metodę kolekcji, która przyjmujeObject o
jako parametr. Dlatego usuwa obiekt3
. Ale w obiekcie arrayList jest on nadpisywany przez indeks 3, a zatem czwarty element jest usuwany.Zgodnie z tą samą logiką usuwania obiektów null jest usuwany w obu przypadkach w drugim wyjściu.
Aby usunąć liczbę,
3
która jest obiektem, będziemy musieli jawnie przekazać 3 jakoobject
.Można to zrobić przez rzutowanie lub owijanie za pomocą klasy opakowania
Integer
.Na przykład:
źródło