Equals() vs ==

0

dlaczego metoda equals() i operator == są równoważne (tzn. sprawdzają równość w rozumieniu przechowywania zmiennych w parmięci) w przypadku innych obiektów oprócz Stringa?

0
  1. Nawet nie wiedziałem, ze w Jawce też tak jest;
  2. Zwyczajnie, żeby było prościej, bo stringi to coś co relatywnie często się porównuje.
0

Jak to sa rownowazne w przypadku innych obiektow oprocz Stringa? Chyba chodzilo o klasy? I wcale nie sa rownowazne, kazda, nietrywialna klasa powinna miec zaimplementowany equals na swoj sposob. Przyklad BigDecimal:

    @Override
    public boolean equals(Object x) {
        if (!(x instanceof BigDecimal))
            return false;
        BigDecimal xDec = (BigDecimal) x;
        if (x == this)
            return true;
        if (scale != xDec.scale)
            return false;
        long s = this.intCompact;
        long xs = xDec.intCompact;
        if (s != INFLATED) {
            if (xs == INFLATED)
                xs = compactValFor(xDec.intVal);
            return xs == s;
        } else if (xs != INFLATED)
            return xs == compactValFor(this.intVal);

        return this.inflated().equals(xDec.inflated());
    }
0

Nie no, chodzi mi o to że equals() dla Stringów sprawdza "zawartość" różnych instancji Stringa, w sensie czy mają te same znaki, a operator == sprawdza czy to są de facto te same egzemplarze Stringa. I to jest ok, ale przy porównywaniu obiektów innej klasy - jakichś tam swoich zdefiniowanych, operatory te są równoważne, i metoda equals() w zasadzie nie robi tego co powinna z założenia.

1

Formalnie to to są dwie różne operacje:
== porównuje referencje do obiektów. Czyli w uproszczeniu, czy wskazuja to samo miejsce w pamięci.
.equals() wykonuje logiczne porównanie - czyli czy są jakoś równe. Można zresztą samemu przedefiniować co to znaczy równe dla własnych klas.

Stringi należy porównywać przez .equals - jeśli chcemy sprawdzić czy mają taką samą zawartość.
Czasem z różnych powodów (optymalizacyjnych) == działa (daje takie same wyniki) jak equals. Kompilator javy czasem potrafi ogarnąć, że pewne obiekty są takie same i powinny mieć to samo miejsce w pamięci. Ale nie należy na tym polegać.

Z tych optymalizacji wychodzi niestety niezły trolling.

public class TrollJava {
    public static void main(String[] args) {
        String a1 = "aaa";
        String b1 = "aaa";
        System.out.println(a1 == b1); //true
        String a2 = "aa";
        String b2 = "a";
        String a3 = a2 + b2;//aaa
        String b3 = b2 + a2;//aaa
        System.out.println(a3 == b3); //false
        Integer x1= 42;
        Integer y1= 42;
        System.out.println(x1 == y1); //true
        Integer x2= 142;
        Integer y2= 142;
        System.out.println(x2 == y2); //false
    }

}

To kompilacja na javac/java 10 i 8. Te wyniki mogą się zmienić w nowszych wersjach.

equals() natomiast będą we wszystkich przypadkach wyżej dawać true. Pewnie zawsze.

1

Pewnie domyslnie equals w klasie Object po ktorej wszystko dziedziczy robi to co == a Ty musisz swoja metode napisac pod kazda klase

0

== służy do porównywania zawartości zmiennej. Zmienna może zawierać typ podstawowy (jak int, long, char, byte, etc) lub referencję do obiektu. Porównywanie typów podstawowych jest oczywiste. Porównanie referencji sprawdza czy referencje wskazują na ten sam obiekt. new Klasa() == new Klasa() zawsze zwróci false, bo mamy tutaj stworzone dwa obiekty. Bez słówka new nie wiadomo czy mamy do czynienia z dwoma różnymi obiektami (tzn jest to szczegół implementacyjny), więc nie należy się sugerować wynikami porównań typu "abc" == "abc". Jeśli już to wymuśmy tworzenie nowych obiektów: new String("abc") == new String("abc") zawsze zwróci false.
https://www.ideone.com/h9lgoS

equals to w zasadzie metoda jak każda inna, oprócz tego że istnieje w klasie Object, więc wszystkie obiekty mają tę metodę. Domyślna implementacja equals znajdująca się w klasie Object zachowuje się tak jak operator ==. Niektóre klasy nadpisują equalsa, np String, a niektóre klasy nie nadpisują equalsa, np tablice (przez co odpalanie equals na tablicach nie ma sensu i trzeba użyć java.util.Arrays.equals by porównanie miało sens).

0

Tak jak kolega powyżej napisał. String ma z automatu zrobionego equalsa, żeby porównywal zawartość. Jak masz inną klasę np. stworzyles sobie klasę User, która ma jakieś tam pola, to domyślnie equals dla tej klasy porównuje miejsca w pamięci tak jak operator ==. Dobrą praktyką jest nadpisać metodę equals w tej klasie User, żeby porównywała czy rzeczywiście pole jednego i drugiego obiektu typu User maja te same wartości czyli np czy name = ''Marek''. Każde środowisko jak np. Intellij pozwala na automatyczną generację tej metody. Należy również wtedy pamiętać żeby również nadpisać metodę hashcode, temat trochę bardziej skomplikowany. Zachęcam do poczytania.

0
jarekr000000 napisał(a):

Formalnie to to są dwie różne operacje:
== porównuje referencje do obiektów. Czyli w uproszczeniu, czy wskazuja to samo miejsce w pamięci.
.equals() wykonuje logiczne porównanie - czyli czy są jakoś równe. Można zresztą samemu przedefiniować co to znaczy równe dla własnych klas.

Stringi należy porównywać przez .equals - jeśli chcemy sprawdzić czy mają taką samą zawartość.
Czasem z różnych powodów (optymalizacyjnych) == działa (daje takie same wyniki) jak equals. Kompilator javy czasem potrafi ogarnąć, że pewne obiekty są takie same i powinny mieć to samo miejsce w pamięci. Ale nie należy na tym polegać.

Ok, czaję. Equals() działa jak powinien tylko przy porównywaniu Stringów, a dla swoich klas trzeba przesłaniać tę metodę. Też zauważyłem, że przy przypisywaniu tego samego tekstu do różnych Stringów kompilator potraktuje je jako ten sam obiekt. De facto zastanawiam się, czy to może być kłopotliwe w niektórych sytuacjach? Jak się skorzysta z operatora new, wtedy już traktuje takie identyczne Stringi oddzielnie.

0

Ok, czaję. Equals() działa jak powinien tylko przy porównywaniu Stringów, a dla swoich klas trzeba przesłaniać tę metodę.

Nie przesłaniać (shadow), a nadpisywać (override, jest nawet taka adnotacja przy nadpisywaniu metod).

Też zauważyłem, że przy przypisywaniu tego samego tekstu do różnych Stringów kompilator potraktuje je jako ten sam obiekt. De facto zastanawiam się, czy to może być kłopotliwe w niektórych sytuacjach?

Stałe Stringi są wrzucane do puli stałych i przez to dwa takie same stałe Stringi w czasie wykonania są jednym obiektem. Pula stałych to angielsku constant pool: What is the purpose of the Java Constant Pool?

Kłopot jest więc taki, że odpalanie == na stałych bez obeznania z pulą stałych prowadzi do niewłaściwych wniosków.

Operator == na referencjach robi zawsze to samo - sprawdza czy obie referencje wskazują na ten sam obiekt. Jakiekolwiek inne wnioski są błędne.

0

TL;DR

Tak, są równoważne. Dla klas które nie mają nadpisanego equals.


Object o1 = new Object();
Object o2 = o1;
Object o3 = new Object();

System.out.println("o1 == o2: "+(o1 == o2));
System.out.println("o1 == o3: "+(o1 == o3));
System.out.println("o1.equals(o2): "+(o1.equals(o2)));
System.out.println("o1.equals(o3): "+(o1.equals(o3)));

// wynik

o1 == o2: true
o1 == o3: false
o1.equals(o2): true
o1.equals(o3): false

https://ideone.com/llilx2

Wynika to z tego jak jest zaimplementowane equals w Object - porównuje hash z referencji (edit: albo samą referencję):

The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).

Szczególnie inne jest zachowanie w String i Integer które potrafią sobie "cache'ować" wartości.
W klasach nadpisujących equals zwykle porównujemy coś więcej bo bardziej nam zależy na wartościach wewnątrz obiektu aniżeli na tym gdzie one leżą.

1 użytkowników online, w tym zalogowanych: 0, gości: 1