Object hash-codes and comparisons

Argentum Objects got two methods: getHash and equals. They look much like Java, but there are multiple nuances.

Hash-codes

  1. The default Object.getHash simply returns hash-code based on the object's address. Since Argentum never moves object in memory (no GC) this hash-code is stable and unique for the duration of object's lifetime. And also they are crazy fast. So the actual reason to override the getHash method in your class is when your objects must be differentiated by content, not by instance address (best example is Strings).
  2. Argentum objects can be frozen or mutable, and for frozen objects it is guaranteed, that their hash-codes never change. That's why Argentum runtime evaluates hash-code for such objects exactly once.
  3. The cached hash-value is stored inside the object's header. All mutable objects already have a field that holds the parent pointer. Since this field is not used for shared-frozen objects, it has got another role - hash-value cash). So this cash costs nothing.
  4. There is a function sys_hash(*Object) int that can be called only on frozen objects.
    • It checks if hash already cached, and if it is, returns it immediately.
    • If not, it calls Object.getHash and stores its result in the object header for the future use.
    • So do call it wherever you need a frozen object's hash. Usually it's as fast as a single memory read.
  5. You can directly call Object.getHash, and it's your only option for mutable objects, and this is totally reasonable, b/c these objects can be modified and as such their hash-codes must be recalculated every time.

Comparisons

  1. You don't need to manually call equals method, because Argentum has a built-in comparison operator a == b.
  2. For optional pointer types this operator handles null/empty values.
  3. For non-null pointers it compares pointers and immediately returns true if they point to the same object.
  4. For pointers to objects of different runtime types it immediately returns false.
  5. When called on shared-frozen objects, it checks for stored hash values and immediately returns false on their mismatch.
  6. So it actually calls the Object.equals method only:
    • for objects at different addresses,
    • if they are non-nulls,
    • and if they are of the same type,
    • and:
      • if they are mutable,
      • or if they have no cached hash-values
      • or if their hashes matched.
  7. In another words:
    • you don't have to add these checks in your equals implementation,
    • the actual equals method is called not that frequently.
  8. The default Object.equals always returns false, so by default objects get compared by addresses. And for most classes it's the best way of comparison.
  9. Standard String.equals compares string bytes.

String literals

  1. Compiler combines the same string literals and stores them once, regardless of their module of origin.
    • This reduces the executable size.
    • This makes their comparisons as fast as a simple pointer-to pointer comparison.
  2. Compiler calculates hashes for all literal strings at compile time and stores them in the object image in the data segment. So all hash-related lookups by literal strings, all searches, and comparisons are performed at the maximum possible speed.

Leave a Reply

Your email address will not be published. Required fields are marked *