To understand the behaviour it is important to understand how Hashmaps work. Hashmaps use hash code value returned by the key objects (String objects in this case) to store them into appropriate memory compartments referred to as buckets. So, when retrieving the value associated with it hashmaps need to locate the compartment where the key is stored and then return the value against that key. Hashmaps identify the compartment from the hash code value of the key.
Now, imagine, if I have two objects which have same hash code value, what would happen? In such case the hashmap needs to know first whether both objects are same because if they are, then it would mean only one entry in the map and the existing value associated with that key will be replaced with the new value. But, merely having same hash code does not mean both keys are equal. So, the equality is determined by calling .equals() method on the key objects. If .equals() returns true then the objects are equal and in such a case the hashmaps need to update the value for the existing entry. But what if .equals() returns false? In that case both objects are different and should be stored as separate entries. So, they are stored side by side in the same compartment. So, when retrieving the value, the input key's hashcode is used to reach the compartment where it is stored and then if the compartment contains more than one objects then the input key is checked for equality with each object in the compartment and if matched then the associated value is returned.
Now, lets apply the above theory to the code you have. String objects are equal if their contents are equal. And by rule, if two objects are equal they should return same hash code. But remember, converse is not true. If two objects return same hash code that does not require them to be equal. This seems confusing at first but you can get over it in a few iterations. Two strings with same contents are equal and return same hash code even if they are physically different objects and hence when used as key in hashmap would always map to the same entry. And hence the behaviour.
The String class overrides the default equals() method which says two objects are equal if they have same references with the one which relies on the contents for equality. And it can do so because Strings are immutable. But, StringBuffer does not do that. It still relies on reference equality.