diff --git hash.c hash.c
index 1380167d08..a15cb0efcc 100644
--- hash.c
+++ hash.c
@@ -2415,6 +2415,82 @@ rb_hash_eql(VALUE hash1, VALUE hash2)
     return hash_equal(hash1, hash2, TRUE);
 }
 
+
+static int
+eqq_i(VALUE key, VALUE val1, VALUE arg)
+{
+    struct equal_data *data = (struct equal_data *)arg;
+    st_data_t val2;
+
+    if (!st_lookup(data->tbl, key, &val2)) {
+	data->result = Qfalse;
+	return ST_STOP;
+    }
+    if (!rb_funcall(val1, idEqq, 1, (VALUE)val2)) {
+	data->result = Qfalse;
+	return ST_STOP;
+    }
+    return ST_CONTINUE;
+}
+
+static VALUE
+recursive_eqq(VALUE hash, VALUE dt, int recur)
+{
+    struct equal_data *data;
+
+    if (recur) return Qtrue;	/* Subtle! */
+    data = (struct equal_data*)dt;
+    data->result = Qtrue;
+    rb_hash_foreach(hash, eqq_i, dt);
+
+    return data->result;
+}
+
+/*
+ *  call-eqq:
+ *     hsh === other_hash    -> true or false
+ *
+ *  Equality(===)---Two hashes are equal if they each contain the same number
+ *  of keys and if each key-value pair is equal to (according to
+ *  <code>Object#===</code>) the corresponding elements in the other
+ *  hash.
+ *
+ *     h1 = { "a" => "hoge", "b" => 2 }
+ *     h2 = { "a" => 1, "b" => 2, "c" => 35 }
+ *     { "a" => String, "c" => Integer } === h1     #=> true
+ *     { "a" => String } === h1                     #=> true
+ *     { "a" => Integer, "c" => String } === h1     #=> false
+ *     { "b" => Integer, "c" => (1..100) } === h2   #=> true
+ *
+ */
+
+static VALUE
+rb_hash_eqq(VALUE hash1, VALUE hash2)
+{
+    struct equal_data data;
+
+    if (hash1 == hash2) return Qtrue;
+    if (!RB_TYPE_P(hash2, T_HASH)) {
+	if (rb_respond_to(hash2, idTo_hash)) {
+	    return rb_hash_eqq(hash1, to_hash(hash2));
+	}
+	else {
+	    return Qfalse;
+	}
+    }
+    if (RHASH_EMPTY_P(hash1))
+	return RHASH_EMPTY_P(hash2) ? Qtrue : Qfalse;
+    if (RHASH_SIZE(hash1) > RHASH_SIZE(hash2))
+	return Qfalse;
+    if (!RHASH(hash1)->ntbl || !RHASH(hash2)->ntbl)
+        return Qtrue;
+    if (RHASH(hash1)->ntbl->type != RHASH(hash2)->ntbl->type)
+	return Qfalse;
+
+    data.tbl = RHASH(hash2)->ntbl;
+    return rb_exec_recursive_paired(recursive_eqq, hash1, hash2, (VALUE)&data);
+}
+
 static int
 hash_i(VALUE key, VALUE val, VALUE arg)
 {
@@ -4696,6 +4772,7 @@ Init_Hash(void)
     rb_define_method(rb_cHash, "to_proc", rb_hash_to_proc, 0);
 
     rb_define_method(rb_cHash, "==", rb_hash_equal, 1);
+    rb_define_method(rb_cHash, "===", rb_hash_eqq, 1);
     rb_define_method(rb_cHash, "[]", rb_hash_aref, 1);
     rb_define_method(rb_cHash, "hash", rb_hash_hash, 0);
     rb_define_method(rb_cHash, "eql?", rb_hash_eql, 1);
diff --git test/ruby/test_hash.rb test/ruby/test_hash.rb
index 088178defa..116993c8a1 100644
--- test/ruby/test_hash.rb
+++ test/ruby/test_hash.rb
@@ -1107,6 +1107,55 @@ def o.eql?(x); false; end
     assert_not_send([@cls[], :eql?, o])
   end
 
+  def test_eqq
+    user = { id: 1, name: "homu", age: 14 }
+    assert_operator({ id: 1 }, :===, user)
+    assert_operator({ id: 1, age: 14 }, :===, user)
+    assert_operator({ id: Integer }, :===, user)
+    assert_operator({ name: String, age: Integer }, :===, user)
+    assert_operator({ name: /m/ }, :===, user)
+    assert_operator({ number: nil }, :===, { number: nil })
+    assert_operator({}, :===, {})
+    obj = Object.new
+    def obj.=== other
+      true
+    end
+    assert_operator({ id: obj }, :===, user)
+    a = { name: String }
+    a[:a] = a
+    assert_operator(a, :===, a)
+    b = { name: "homu" }
+    b[:a] = b
+    assert_operator(a, :===, b)
+
+    o2 = Object.new
+    def o2.to_hash
+      { name: "homu", age: 14 }
+    end
+    assert_operator({ name: /^h/ }, :===, o2)
+
+    assert_not_operator({ id: 2 }, :===, user)
+    assert_not_operator({ name: Integer }, :===, user)
+    assert_not_operator({ number: 42 }, :===, user)
+    assert_not_operator({ number: nil }, :===, {})
+    assert_not_operator({ number: 42 }, :===, {})
+    assert_not_operator({ number: 42 }, :===, 42)
+    assert_not_operator({ number: 42 }, :===, [])
+    assert_not_operator({}, :===, user)
+    assert_not_operator({}, :===, 42)
+    assert_not_operator({}, :===, { name: "homu" })
+    obj2 = Object.new
+    def obj2.=== other
+      false
+    end
+    assert_not_operator({ id: obj2 }, :===, user)
+    a = { name: Integer }
+    a[:a] = a
+    b = { name: "homu" }
+    b[:a] = b
+    assert_not_operator(a, :===, b)
+  end
+
   def test_hash2
     assert_kind_of(Integer, @cls[].hash)
     h = @cls[1=>2]
