A set in java never allows duplicates, but it takes StringBuffer objects with the same argument. What for?

public static void main(String[] args) { HashSet set = new HashSet(); set.add(new StringBuffer("abc")); set.add(new StringBuffer("abc")); set.add(new StringBuffer("abc")); set.add(new StringBuffer("abc")); System.out.println(set); } 

Output:

 [abc,abc,abc,abc] 

Here, in the code above, I added the StringBuffer("abc") object many times, and Set adds it, but Set never adds duplicates.

+6
source share
6 answers

StringBuffer does not override Object#equals() and Object#hashCode() , therefore, the identification of StringBuffer instances StringBuffer based not on the contents of the buffer, but on the address of the object in memory. *


* The identity is based on an in-memory address that is not strictly required by JLS, but is the result of a typical implementation of Object#hashCode() . From JavaDoc:

As reasonably practical, the hashCode method defined by the Object class returns different integers for different objects. (This is usually done by converting the internal address of the object to an integer, but this implementation method is not required by the Java β„’ programming language.)

+13
source

StringBuffer does not override either equals or hashCode - so each object is equal only to itself.

This makes sense, since the StringBuffer very β€œredesigned” - and equality can cause problems when two mutable objects are equal to each other, since then you can change. Using modified objects as keys on a map or part of a set can cause problems. If you insert one of them after pasting into the collection, this invalidates the entry in the collection, as the hash code may change. For example, on a map you cannot even find a value with the same object as the key, since the first test is a hash code.

StringBuffer (and StringBuilder ) are designed to be very temporary objects - create them, add to them, convert them to strings, and then you're done. Each time you add them to collections, you need to take a step back and see if it really makes sense. Sometimes this can happen, but usually only when the collection itself is short-lived.

You should take this into account in your own code when overriding equals and hashCode - it is very rarely a good idea for equality to rely on any mutable aspect of an object; this makes the class harder to use correctly and can easily lead to subtle errors that can take a long time to debug.

+8
source

Have you ever seen the equals () method (or lack thereof) in a StringBuffer? The answer lies with you.

A Set or, for that matter, any hash-based collection depends on the contract exposed by the equals () method and hashcode () on the object for their behavior characteristics.

In your case, since StringBuffer does not override these methods, each instance of StringBuffer that you create is different from ie the new StringBuffer ("abc"). == new StringBuffer ("abc") returns false.

I am curious why someone will add a StringBuffer to the set.

+1
source

Most modified objects do not assume that if they contain the same data, they are the same. Since they are mutable, you can change the contents at any time. that is, it can be the same, but not later, otherwise it can be different, but be the same later

BTW You should not use StringBuffer if StringBuilder is an option. StringBuffer was replaced over ten years ago.

+1
source

The two StringBuffer objects are different objects, despite the same arguments. Therefore, a HashSet simply adds StringBuffers instead of ignoring duplicates.

0
source

A set of hashes works with buckets. It stores the values ​​in these "buckets" according to their hash code. A bucket may contain several elements, depending on whether these members are equal using the equals(Object) method.

So, let's say we build a hash set with 10 buckets for the sake of argument, and add integers 1, 2, 3, 5, 7, 11, and 13 to it. The hash code for int is just int. The result is something like this:

  • (empty)
  • 1, 11
  • 2
  • 3, 13
  • (empty)
  • 5
  • (empty)
  • 7
  • (empty)
  • (empty)

The traditional way to use a set is to look and see if an element is in this set. Therefore, when we say: "In this set of 11?" the hash set will be modulo 11 by 10, get 1 and look in the second bucket (we, of course, start our buckets with 0).

It is really very important to make sure that members belong to a set or not. If we add another 11, the hash set will see if it is already there. He will not add it again, if so. It uses the equals(Object) method to determine that, and, of course, 11 equals 11.

The hash code for a string like "abc" depends on the characters in that string. When you add a repeating line, "abc", the hash set will look in the right bucket, and then use the equals(Object) method to see if the element is already there. The equals(Object) method for a string also depends on characters, so "abc" is equal to "abc".

If you use a StringBuffer, each StringBuffer has a hash code and equality based on its object id. It does not override the base methods equals(Object) and hashCode() , so each StringBuffer looks at the hash set as a different object. They are not really duplicates.

When you print StringBuffers in the output, you call the toString () method on StringBuffers. This makes them look like repeating lines, so you see this output.

This is why it is very important to override hashCode() if you override equals(Object) , otherwise Set will look in the wrong bucket and you will get very strange and unpredictable behavior!

0
source

Source: https://habr.com/ru/post/948674/


All Articles