Equality methods and hashcodes
There are several different ways in which two objects can be equal to each other, and
JS.Class provides equality methods with different semantics. It’s important to know
the difference as there are parts of the framework that expect certain equality methods
to be used to make integration as painless as possible.
If you want to use an object as a key in a Hash, you’ll need to override
its equals() and hash() methods in a consistent way; see below for more information.
Built-in operators: == and ===
JavaScript has two built-in equality operators, == for equality and === for identity.
The expression foo === bar returns true iff foo and bar refer to the same object.
For primitive values (numbers, strings, boolean values) this implies that both must have
the same value and the same type. So '5' === '5' is true, but '5' === 5 is false.
Similarly false === null and false === 0 are false.
The equality operator == is more lenient. For objects it has the same effect as ===,
but for primitives it performs type casting to figure out whether two values are
“equivalent”. So, '5'==5 is true, as are false==null and false==0.
Sort position equality: object.eq(other)
This method is provided by the Comparable module and returns true
iff object and other have the same sort priority. Two objects may appear equal in
terms of eq() but may not be completely meaningfully equal. It simply means object
is neither “less than” nor “greater than” other, and they may appear in either order
in a sorted collection. Though not used directly, the related method compareTo() is
used by Hash and SortedSet for sorting objects.
Equivalence: object.equals(other)
This returns true iff object and other are meaningfully equal. The default implementation
of this method in Kernel just uses === to compare the objects;
classes may override this method to provide more meaningful comparison. For example,
Set#equals() returns true iff used to compare two sets that contain the
same members. The following classes expect objects to implement equals():
Hashusesequals()to make sure keys are unique, falling back to===if they keys do not implementequals(). See alsoobject.hash()below.Setusesequals()to make sure elements are unique, falling back to===if the elements do not implementequals().
If you implement equals() in one of your own classes, it should obey these rules:
- Reflexivity:
x.equals(x)must be true - Symmetry:
x.equals(y)should be true iffy.equals(x)is true - Transitivity: if
x.equals(y)is true andy.equals(z)is true, thenx.equals(z)is true - Consistency:
x.equals(y)must consistently return true or false as long as the state ofxandyis not modified - No object is equal to
null;x.equals(null)is always false
You must also override hash() such that if x.equals(y) is true, then
x.hash() === y.hash() is also true. Otherwise, your objects will not work as a
key in a Hash.
Hash equality: object.hash()
The hash() method is used internally by Hash to improve key search
performance by splitting the stored pairs into “buckets” and assigning each bucket a
hashcode. (See Wikipedia for more on how hashtables work.)
When you ask a Hash for the value for a given key, it converts the key to a hashcode
to figure out which bucket to look in.
The default implementation of hash() in Kernel produces a different
value for every object. If you implement equals() in your class such that two objects
can be considered equal, you will need to make sure that two equal objects return the
same hashcode; two equal objects should return the same value when given as keys to a
Hash, but if they have different hashcodes, the hash will look in different places to
find their values.
So, if you implement equals() in a class, you must also implement hash() such that:
x.hash()takes no parameters and returns a string based on the state ofx- Calling
x.hash()returns the same value every time as long as the state ofxdoes not change - If two objects are equal according to
x.equals(y), thenx.hash()andy.hash()return the same value - If two objects are not equal, they do not necessarily return different hash values, though this will improve the performance of any hashtables that use them