In line 12, you are comparing String objects with == instead of with equals().
Note that == will only return true when you use it on objects (rather than primitive values) when the two operands on both sides of the == refer to the exact same object. It will return false if the operands refer to different objects, even if those objects seem to contain the same value.
What makes it a little bit more complicated is that Java reuses literal strings. So if you use for example the string literal "a" multiple times in a program, then Java is smart enough to make just one String object that contains "a", and it uses that object every time you use "a" in your source code. That's why something like this will print true:
Note that if you explicitly create a new String object, the result would be false:
To make your code do what you expect, use equals() in line 12 to compare ((Dog)o).s and this.s instead of ==.
Another thing that is a bit strange is that you're using the name list for your variable that is actually a Set. A Set is not a list. You should think of a Set as a bag that contains elements in no particular order, which is different from a list (which does have a defined order).
You can run into problems making equals() and hashCode() dependent on mutable fields, and this is one example why.
Although in #2 you're looking for the actual object that's in the Set (so an equals() comparison will succeed), it doesn't have the same hash code as it had when you added it to the Set. So it's not in the bucket that it "should" be in, and the HashSet algorithm won't find it. If the hash code changes after it's been added the HashSet has no way of knowing this.
#3 the same reason - the hash code does not match the value it was added with.
#5 is false because although it has the right hash code, it will fail an equals() comparison, because the original d no longer contains "aaa".
Just another example of how necessary it is to appropriately override hashCode() and equals() methods of the Object class, especially for hashed collections. It is dangerous to base the implementation of hashCode() on mutable fields of the object.
Yes, because you changed the state of the mutable object, its hash code changed, so you are looking in the wrong bucket. You can repeat the procedure with new Dog("a") and again find you are looking in the wrong bucket. You can tell which bucket the references go into because you know they go into buckets[Math.abs(h % c)] where h is the hash code and c is the capacity, which defaults to 16. The implementation probably users something different from abs and %, as discussed in this thread.
I thought that was a well‑known problem, but I couldn’t find it in the Java Tutorials nor the Specification nor the API for HashSet. It is in Odersky Spoon and Venners, however.