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()
:
Hash
usesequals()
to make sure keys are unique, falling back to===
if they keys do not implementequals()
. See alsoobject.hash()
below.Range
usesequals()
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()
.Set
usesequals()
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 ofx
andy
is 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 ofx
does 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