Równość i Identyczność Integerów
Dowiedz się czym jest równość i identyczność Integerów w Javie. Co lepiej używać do ich tworzenia: konstruktora czy statycznej metody fabrycznej valueOf?

Równość a Identyczność wokół nas
Równość (ang. Equality) sprawdzamy poprzez porównanie stanów dwóch obiektów. Przeprowadźmy eksperyment myślowy mający na celu porównanie dwóch butelek z cieczą z założeniem, że objętość cieczy w butelkach będzie taka sama. W jednej butelce jest woda, w drugiej sok (patrz obrazek poniżej). Czy te butelki z cieczą są sobie równe? Nie są: pomimo, że obydwa znajdują się w butelce, ciecz w nich umieszczona nie jest tego samego rodzaju (stan się różni). Natomiast gdy porównamy ze sobą dwie butelki z wodą, możemy stwierdzić, że są one sobie równe: obie znajdują się w butelce oraz obie są cieczą tego samego rodzaju (stan jest taki sam).

Identyczność (ang. Identity) sprawdzamy poprzez porównanie dwóch obiektów, z czego drugi obiekt może być tym samym obiektem co pierwszy. Dwie różne butelki z wodą są równe, ale nie są identyczne: są to dwa odrębne obiekty. Wyobraź sobie, że położyłeś butelkę wody na stole, a podczas Twojej nieuwagi inny domownik przełożył tę butelkę na półkę w innym pokoju. Zaczniesz szukać butelki z wodą: identycznej, czyli tej która stała na stole. Nie interesują Cię inne butelki (nie są identyczne) tylko ta jedna, która stała wcześniej na stole. Gdy porównamy butelkę wody stojącą wcześniej na stole z tą stojącą teraz na półce w drugim pokoju po przełożeniu zauważymy, że są one równe (ta sama butelka i rodzaj cieczy: woda) i identyczne (to jeden i ten sam obiekt).

Równość a Identyczność w Javie
W Javie sprawa wygląda podobnie. Równość sprawdzamy metodą equals klasy java.lang.Object
natomiast identyczność operatorem ==
.

Identyczność sprawdza czy dwa obiekty wskazują do tego samego miejsca w pamięci komputera, czyli czy jest to jeden i ten sam obiekt. Porównuje się w tym celu referencje do obiektów, czyli adresy obiektów w pamięci komputera. Gdy adres jest taki sam, porównywane obiekty są identyczne.
Równość sprawdza, czy dwa obiekty są sobie równe. Domyślnie jest sprawdzana po prostu identyczność jednak możemy nadpisać metodę equals
i podać własne kryteria określające czy dany obiekt jest równy innemu (np. porównując jego pola z polami drugiego obiektu).

equals
w klasie java.lang.Object
Kiedy tworzymy obiekt, komputer przechowuje go w swojej pamięci, gdzie przypisywany jest mu adres. Dzięki temu komputer może go później odnaleźć.
Równe obiekty muszą posiadać również równe wartości hash code zwracane przez metodę hashCode klasy
java.lang.Object
.Nie działa to w drugą stronę: obiekty zwracające tę samą wartość hash code nie muszą być równe.
Czym jest Integer
Klasa java.lang.Integer
reprezentuje liczby całkowite: opakowuje wartość prymitywnego typu int
przez co jest nazywana klasą opakowującą (ang. wrapper class). Innymi słowy klasa Integer
posiada jedno pole typu int
.

int
w klasie java.lang.Integer
Czym jest IntegerCache
Jaka jest różnica między new Integer(5)
a Integer.valueOf(5)
?
Pierwsze to konstruktor, który zawsze tworzy obiekt. Został on zdeprecjonowany w Javie 9, czyli jest przestarzały i nie powinno się go używać. W Javie 16 został oznaczony do usunięcia, co oznacza, że w następnych wersjach Javy zostanie usunięty.

java.lang.Integer
przyjmujący parametr typu int
Drugie to statyczna metoda fabryczna (ang. static factory method) zwracająca instancję klasy. Metoda ta korzysta z IntegerCache
, dzięki któremu obiekty Integer
nie zawsze są tworzone. IntegerCache
może zwrócić obiekt Integer
, który został uprzednio zcache'owany (stworzony i umieszczony w cache do późniejszego użytku).

valueOf
klasy java.lang.Integer
korzystająca z IntegerCache
IntegerCache
(dodany w Javie 5) to prywatna, statyczna, wewnętrzna klasa znajdująca się w klasie Integer
. Cache'uje obiekty od -128 do 127 włącznie co zwiększa wydajność i zmniejsza wykorzystanie pamięci komputera. Cache ten jest inicjalizowany przy pierwszym użyciu, zaś jego rozmiar można zmodyfikować poprzez użycie flagi: -XX:AutoBoxCacheMax=<size>
. Parametr <size>
to liczba maksymalna: domyślnie 127. Jeżeli określisz rozmiar cache na liczbę mniejszą niż 127, zostanie ona nadpisana liczbą 127.
Autoboxing, czyli automatyczna zamiana typu prymitywnego w odpowiadający mu obiekt korzysta z mechanizmu cache'owania.
// To jest autoboxing
Integer fiveAutoboxing = 5;
// Autoboxing w rzeczywistości wywołuje poniższą metodę
Integer fiveStaticFactoryMethod = Integer.valueOf(5);
IntegerCache w praktyce
Co wypisze w konsoli poniższy kod?
Integer a = 20;
Integer b = 20;
System.out.println("Czy a i b są równe: " + a.equals(b));
System.out.println("Czy a i b są identyczne: " + (a == b));
Integer c = 200;
Integer d = 200;
System.out.println("Czy c i d są równe: " + c.equals(d));
System.out.println("Czy c i d są identyczne: " + (c == d));
Używając autoboxingu tworzymy 4 zmienne całkowite: a
i b
o wartości 20 oraz c
i d
o wartości 200.
Jak już wspomniałem wcześniej, operator ==
w Javie sprawdza czy porównywane obiekty wskazują do tego samego miejsca w pamięci. Jeżeli będzie to ten sam obiekt w pamięci, otrzymamy true
, w przeciwnym wypadku false
.
Autoboxing używa mechanizmu cache'owania dla wartości od -128 do 127. W związku z tym a
i b
będzie jednym i tym samym obiektem (zwróconym z cache). Natomiast c
i d
będą różnymi obiektami, ponieważ każdy obiekt Integer
większy od 127 i mniejszy od -128 będzie nowym obiektem w pamięci, gdyż statyczna metoda fabryczna tworzy nowy obiekt w tym zakresie!

Cache w innych obiektach opakowujących
Mechanizm cachowania w statycznej metodzie fabrycznej valueOf
występuje nie tylko w Integerach, ale także w innych klasach opakowujących typy prymitywne:
Byte
z mechanizmemByteCache
Short
z mechanizmemShortCache
Long
z mechanizmemLongCache
Character
z mechanizmemCharacterCache
ByteCache
, ShortCache
i LongCache
również cache'ują obiekty od -128 do 127 włącznie. Natomiast CharacterCache
cache'uje znaki unicode od 0 do 127 włącznie, czyli od znaku \u0000
do znaku \u007F
.
Tylko rozmiar IntegerCache
może być modyfikowany przez flagę.
Float
i Double
nie korzystują z mechanizmu cache'owania i zawsze tworzą nowy obiekt.
Boolean
nie posiada cache, ponieważ wystarczą mu tylko dwa stworzone wcześniej obiekty: TRUE
i FALSE
, które zwraca ze statycznej metody fabrycznej valueOf
.
GitHub
Kod źródłowy jest udostępniony na GitHubie. Poniżej link 🙂
Podsumowanie
Identyczne obiekty wskazują do tego samego miejsca w pamięci komputera, czyli ich referencje (adresy w pamięci) są takie same: jest to jeden i ten sam obiekt. Równe obiekty posiadają ten sam stan, czyli ich pola są równe. Identyczne obiekty zawsze są równe. Równe obiekty nie zawsze są identyczne. Odnosi się to zarówno do Integerów jak i innych obiektów.
Do tworzenia Integerów używaj statycznej metody fabrycznej valueOf
np. Integer.valueOf(1)
lub autoboxingu, które korzystają z IntegerCache
: mechanizmu zwiększającego wydajność i zmniejszającego wykorzystanie pamięci komputera. Autoboxing tak naprawdę wywołuje statyczną metodę fabryczną valueOf
. Konstruktor Integera jest zdeprecjonowany od Javy 9 i oznaczony do usunięcia od Javy 16. Nie powinien być używany i w kolejnych wersjach Javy zostanie usunięty.
Liczba komentarzy:
Zaloguj się lub dołącz do społeczności Javowców, aby móc uczestniczyć w dyskusji 🙂