How does the equality test operator ("==") work, are there alternatives for it, and in what situations should it be used?
1 answer
Operator "=="
In Java, the operator "==" returns a boolean value - the result of comparison of references to objects (except primitives), i.e. This operator does not compare for equality the internal contents of objects , but simply checks whether the references point to the same object. Thus, when comparing objects in Java, the operator "==" returns true only when the links point to the same object.
Comparing Primitives
For primitive types ( byte , short , int , long , char , float and double ), the concept of equality / inequality is rather trivial: first, the operands are propagated (i.e., the arithmetic propagation of the operands occurs to the greatest if necessary, and then direct bit-by-bit comparison (if all bits are equal, then the return value will be true , otherwise - false ).
For example, the value of 48.0f (of type float , respectively) is equal to the value of '0' (whose code is 48 according to the Unicode symbol table) of type char , the value of which implicitly extends to the type of float .
Test case:
char char1 = '0'; int int1 = 48; float float1 = 48.0F; System.out.println(char1 == int1); // true System.out.println(char1 == float1); // true System.out.println(int1 == float1); // true // --------------------------------------------- char char2 = 'A'; int int2 = 65; float float2 = 65.0F; System.out.println(char2 == int2); // true System.out.println(char2 == float2); // true System.out.println(int2 == float2); // true Comparison of real primitives
For real primitives ( float and double ) there are some features that need to be considered. Representation of the fractional part is carried out using a finite number series 2^(-n) (where the value of the number n depends on the size of the mantis , which in turn depends on the specific type: float - 24 бита is 2^(-24) ≈ 6*10^(-8) , that is, 6E-8F , or double - 53 бита - this is 2^(-53) ≈ 10^(-16) , that is, 1E-16 , which is actually a step with which there are values in the representation of these types), and therefore it is not necessary to speak about the exact representation of an arbitrarily taken number.
It goes without saying that two real variables, initialized by the same real literal, will be equal (after all, they were initialized by the same sequence of bits):
float float1 = 0.7F; float float2 = 0.7F; System.out.println(float1 == float2); // true But it is worth starting to perform arithmetic operations with variables of this type, as most likely, the calculation error will start to accumulate (due to the above features of the representation of instances of this type):
float float1 = 0.7F; float float2 = 0.3F + 0.4F; System.out.println(float1 == float2); // false System.out.println(float1); // 0.7 System.out.println(float2); // 0.70000005 How to compare real primitives
Real primitives ( float and double ) should be compared with a certain accuracy. For example, round them to the 6th decimal place ( 1E-6 for double , or 1E-6F for float ), or, preferably, check the absolute value of the difference between them.
Test case:
float float1 = 0.7F; float float2 = 0.3F + 0.4F; final float EPS = 1E-6F; System.out.println(Math.abs(float1 - float2) < EPS); // true Use of pools
As already mentioned, the operator "==" checks whether the links point to the same object , but sometimes this simple expression hides something more than it might seem at first glance.
For more efficient use of memory, Java uses so-called pools: Integer pool , String pool and some others. When we create an object without using the new operation, the object is placed in the pool, and subsequently, if we want to create the same object (again without using new ), the new object will not be created, and we will simply get a link to our object from the pool.
It is worth noting that Integer Pool will be used for any autoboxing, if the value corresponds to the specified range, as opposed to the String Pool , for which interning works for literals.
Integer pool
The Integer pool feature is that it only stores numbers that fit into the byte data type: from -128 to 127 (which is from Java 7 (earlier it was hard-coded inside java.lang.Integer ) and can be extended using the JVM option : -Djava.lang.Integer.IntegerCache.high=size or -XX:AutoBoxCacheMax=size ). For the remaining numbers from Integer 'and the pool does not work.
Test case:
Integer valueFromPool1 = 127; Integer valueFromPool2 = 127; Integer valueNotFromPool1 = 128; Integer valueNotFromPool2 = 128; System.out.println(valueFromPool1 == valueFromPool2); // true System.out.println(valueNotFromPool1 == valueNotFromPool2); // false System.out.println(127 == valueFromPool1); // true System.out.println(128 == valueNotFromPool1); // true System.out.println((Integer)127 == valueFromPool1); // true System.out.println((Integer)128 == valueNotFromPool1); // false String pool
Select the main string pool nuances in Java :
- String literals (in the same / different class (s) and in the same / different package (s)) are references to the same object.
- Strings resulting from addition of constants are calculated at compile time and then see point one.
- Strings created at runtime do NOT refer to the same object.
- The
internmethod in any case returns an object from the pool, regardless of when the string is created, at the stage of compilation or execution.
In the context of these points, the discussion was about "identical" string literals.
Test case:
String hello = "Hello", hello2 = "Hello"; String hel = "Hel", lo = "lo"; System.out.println("Hello" == "Hello"); // true System.out.println("Hello" == "hello"); // false System.out.println(hello == hello2); // true System.out.println(hello == ("Hel" + "lo")); // true System.out.println(hello == (hel + lo)); // false System.out.println(hello == (hel + lo).intern()); // true Read more about interning here .
Alterantivy operator "=="
To compare two objects in Java , there are also methods such as: equals() and hashCode() . The hashCode() and equals() methods are defined in the Object class, which is the parent class for Java objects. Therefore, all Java objects inherit from these methods the default implementation.
Using equals () and hashCode ()
The equals() method, as its name implies, is used to simply check the equality of two objects. The default implementation of this method simply checks two objects by reference for their equivalence, i.e. just compare links.
The hashCode() method is usually used to get a unique integer number, which is actually not true, since the result of this method is an integer of the primitive type int (called a hash code ), obtained by the hash function whose input parameter is the object that calls this method, but the set of possible hash codes is limited to the primitive type int , and the set of objects is limited only our fantasy.
As a result, we have the following:
- if the hash codes are different, then the objects are guaranteed different
- if the hash codes are equal, then the input objects are not always equal (this is due to the fact that the set of objects is more powerful than the hash code , since the set of possible hash codes is limited to the primitive type
int)
Also about these methods can be found here .
Override default behavior.
Since in Java it is impossible to redefine operators (i.e., the behavior of the operator "==" cannot be changed) as, for example, in C ++ or C # , then to compare two objects of the same class according to the algorithm necessary for us, you can override it ( @Override ) methods equals() and hashCode() inside our class and use them.
Thus, creating a custom class should override the methods hashCode() and equals() so that they work correctly and take into account the data of the object. In addition, if you leave the implementation of Object , then, for example, using java.util.HashMap cause problems, since HashMap actively use hashCode() and equals() in their work.
Features when working with real types Float and Double
In Java, NaN 's are incomparable with each other, but there are two exceptions to the work of the Float and Double classes, consider the example of the Float class:
If
float1andfloat2both representFloat.NaN, then theequalsmethod returnstrue, whileFloat.NaN == Float.NaNisfalse.If
float1contains+0.0fwhilefloat2contains-0.0f, theequalsmethod returnsfalse, while0.0f == -0.0freturnstrue.
Test Example 1:
Float float1 = new Float(Float.NaN); Float float2 = new Float(Float.NaN); System.out.println(float1 == float2); // false System.out.println(float1.equals(float2)); // true System.out.println(Float.NaN == Float.NaN); // false // -------------------------------------------------- Double double1 = new Double(Double.NaN); Double double2 = new Double(Double.NaN); System.out.println(double1 == double2); // false System.out.println(double1.equals(double2)); // true System.out.println(Double.NaN == Double.NaN); // false Reference Example 2:
Float float1 = new Float(0.0F); Float float2 = new Float(-0.0F); System.out.println(float1 == float2); // false System.out.println(float1.equals(float2)); // false System.out.println(0.0F == -0.0F); // true // -------------------------------------------------- Double double1 = new Double(0.0); Double double2 = new Double(-0.0); System.out.println(double1 == double2); // false System.out.println(double1.equals(double2)); // false System.out.println(0.0 == -0.0); // true Let's sum up
If you need to check whether two variables refer to the same object, or compare two primitives for equality (but you should keep in mind that real numbers should be compared only with a certain accuracy), then you should definitely use the operator "== "but if your data variables, you should use the method of the corresponding class is necessary to compare the internal contents of an object (or for the user to compare the type of even some special algorithm), relied on by equals() (pr than if it is a custom class, you should override this method and do not forget about the method of hashCode() ).
- fivemagically, what is missing is a couple of lines about primitives - etki
- 3I will add that the Integer cache will be used for any auto-packing, if the value corresponds to the specified range (as opposed to the lines for which the internment works for literals). This range can also be adjusted with the -XX: AutoBoxCacheMax option. - Pavel Parshin
- 3Your answer is unformat for CO. It is inconvenient to use your answer as a goal for closing questions as duplicates, because it’s like sending documents to read: instead of solving a specific problem, you force a multi-book reader to read, everything is blurred. The text is cool, of course, but this is an article, not an answer. You solve a problem that does not exist. For example, who generally has a problem with comparing char and float in real life? This is from the category of "flash knowledge before friends", and even without a sensible explanation. The same problem with the remaining pieces of the answer - unprocessed explanations. - Athari
- one@Discord I understand, and I even agree. Initially, it was all conceived - to clarify everything concisely, but in the course of writing, features began to emerge one by one. Well, and then it became a pity to delete what has already accumulated. - StateItPrimitive
- 2@StateItPrimitive The "as suggested in comments" marks should be deleted, optionally with brackets, this is garbage text. Horror pro "is used to get a unique integer, although in fact it is not quite unique" I would delete without regret. The names of programming languages are not
код. - Athari