16

I don't understand how a set determines when two objects are equal. More specific, when does the add method of a set, really adds a new object, and when doesn't it act a new object, because the object is already in the set ?

For example, I have objects from the following class:

class Action {
  final Function function;
  final String description;

  Action(this.function, this.description);

  call() => function();

  toString() => description;
}

Now I would think that the following set would contain 2 elements, as 2 of them are equal:

void main() {
  Set<Action> actions = new Set()
    ..add(new Action(() => print("a"), "print a"))  
    ..add(new Action(() => print("a"), "print a"))
    ..add(new Action(() => print("b"), "print b"));
}

But instead, this set contains 3 Action objects. See the demo. How can I make sure that equal objects are seen as equal in the set ?

Kasper
  • 10,662
  • 10
  • 38
  • 59

2 Answers2

30

For a comprehensive write-up about operator== in Dart see http://work.j832.com/2014/05/equality-and-dart.html

It just checks if they are equal a == b You can override the == operator to customize this behavior. Keep in mind that also hashCode should be overridden when the == operator is overridden.

class Action {
  @override
  bool operator==(other) {
    // Dart ensures that operator== isn't called with null
    // if(other == null) {
    //   return false;
    // }
    if(other is! Action) {
      return false;
    }
    return description == (other as Action).description;
  }

  // hashCode must never change otherwise the value can't
  // be found anymore for example when used as key 
  // in hashMaps therefore we cache it after first creation.
  // If you want to combine more values in hashCode creation
  // see http://stackoverflow.com/a/26648915/217408
  // This is just one attempt, depending on your requirements
  // different handling might be more appropriate.
  // As far as I am aware there is no correct answer for
  // objects where the members taking part of hashCode and
  // equality calculation are mutable.
  // See also http://stackoverflow.com/a/27609/217408
  int _hashCode;
  @override
  int get hashCode {
    if(_hashCode == null) {
      _hashCode = description.hashCode
    }
    return _hashCode;
  }
  // when the key (description) is immutable and the only
  // member of the key you can just use
  // int get hashCode => description.hashCode
}

try at DartPad

Günter Zöchbauer
  • 558,509
  • 191
  • 1,911
  • 1,506
  • 2
    The argument to `operator==` is never `null` since `null` is checked explicitly before calling `operator==`. You can remove the `other == null` check. – lrn Apr 15 '15 at 11:04
  • Great, thanks a lot! This wasn't what I remembered but it's even more comprehensive. – Günter Zöchbauer Apr 15 '15 at 13:20
0

Here’s an example of testing top-level functions, static methods, and instance methods for equality:

void foo() {} // A top-level function

class A {
  static void bar() {} // A static method
  void baz() {} // An instance method
}

void main() {
  var x;

  // Comparing top-level functions.
  x = foo;
  assert(foo == x);

  // Comparing static methods.
  x = A.bar;
  assert(A.bar == x);

  // Comparing instance methods.
  var v = A(); // Instance #1 of A
  var w = A(); // Instance #2 of A
  var y = w;
  x = w.baz;

  // These closures refer to the same instance (#2),
  // so they're equal.
  assert(y.baz == x);

  // These closures refer to different instances,
  // so they're unequal.
  assert(v.baz != w.baz);
}
Paresh Mangukiya
  • 37,512
  • 17
  • 201
  • 182