diff --git a/spec/std/hash_spec.cr b/spec/std/hash_spec.cr index d098c8f7bf11..684e5af9c0d1 100644 --- a/spec/std/hash_spec.cr +++ b/spec/std/hash_spec.cr @@ -1393,16 +1393,26 @@ describe "Hash" do hash.@indices_size_pow2.should eq(12) end - it "rehashes" do - a = [1] - h = {a => 0} - (10..100).each do |i| - h[[i]] = i - end - a << 2 - h[a]?.should be_nil - h.rehash - h[a].should eq(0) + describe "#rehash" do + it "rehashes" do + a = [1] + h = {a => 0} + (10..100).each do |i| + h[[i]] = i + end + a << 2 + h[a]?.should be_nil + h.rehash + h[a].should eq(0) + end + + it "resets @first (#14602)" do + h = {"a" => 1, "b" => 2} + h.delete("a") + h.rehash + # We cannot test direct equivalence here because `Hash#==(Hash)` does not depend on `@first` + h.to_s.should eq %({"b" => 2}) + end end describe "some edge cases while changing the implementation to open addressing" do diff --git a/src/hash.cr b/src/hash.cr index 24a658dad21d..929e84a1b455 100644 --- a/src/hash.cr +++ b/src/hash.cr @@ -631,6 +631,9 @@ class Hash(K, V) new_entry_index += 1 end + # Reset offset to first non-deleted entry + @first = 0 + # We have to mark entries starting from the final new index # as deleted so the GC can collect them. entries_to_clear = entries_size - new_entry_index