Equality methods and hashcodes
There are several different ways in which two objects can be equal to each
other, and jsclass 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.Rangeusesequals()to figure out whether the endpoints of two ranges are the same in its ownequals()method. It usescompareTo()to figure out whether the end of a range has been found or exceeded while iterating usingforEach().Setusesequals()to make sure elements are unique, falling back to===if the elements do not implementequals().
jsclass does not modify built-in JavaScript classes, but the above classes
do contain methods for comparing Array and Object instances. Two arrays
are considered equal if their elements are all equal, and two objects with no
equals() method are considered equal if they have the same set of keys-value
pairs, much like Hash equality.
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 or as a member of a Set.
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