59

Let's say I have this code in Java:

HashSet<String> wordSet = new HashSet<String>();
String a = "hello";
String b = "hello";
wordSet.add(a);

Would wordSet.contains(b); return true or false? From what I understand, a and b refer to different objects even though their values are the same. So contains() should return false. However, when I run this code, it returns true. Will it always return true no matter where String object b is coming from as long as b contains the value "hello"? Am I guaranteed this always? If not, when am I not guaranteed this? And what if I wanted to do something similar with objects other than Strings?

OkonX
  • 849
  • 1
  • 7
  • 10
  • In your example, a and b actually point to the same object instance. Which may be misleading in tests. Use new String(...) to force different objects. See https://docs.oracle.com/javase/specs/jls/se18/html/jls-3.html#jls-3.10.5 – Joe C. Aug 10 '22 at 00:32

6 Answers6

67

It uses equals() to compare the data. Below is from the javadoc for Set

adds the specified element e to this set if the set contains no element e2 such that (e==null ? e2==null : e.equals(e2)).

The equals() method for String does a character by character comparison. From the javadoc for String

The result is true if and only if the argument is not null and is a String object that represents the same sequence of characters as this object

David W
  • 945
  • 9
  • 21
Aravind Yarram
  • 78,777
  • 46
  • 231
  • 327
8

Actually, HashSet does neither.

Its implementation uses a HashMap, and here's the relevant code that determines if the set contains() (actually it's inside HashMap's getEntry() method):

if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))

which:

  • requires the hashes to equal, and
  • requires either object equality or equals() returns true

The answer is "yes": wordSet.contains(b) will always return true

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • 1
    The OP asked about `HashSet`. The `String` class's `hashCode` method returns equal values for two strings for which `equals` is true. The `String` class's `equals` method returns true if its argument is the same object as the object for which it's called. So the condition that you've quoted is logically equivalent to `equals()`. – Dawood ibn Kareem Jan 23 '12 at 05:20
4

Actually, both a and b refer to the same object, because string literals in Java are automatically interned.

Perception
  • 79,279
  • 19
  • 185
  • 195
  • 1
    ... which is why he/she said 'Will it always return true no matter where String object b is coming from as long as b contains the value "hello"?' – Dawood ibn Kareem Jan 23 '12 at 05:07
  • `From what I understand, a and b refer to different objects even though their values are the same` - Im pretty sure thats not what he thought. – Perception Jan 23 '12 at 05:11
  • 1
    Yeah, sorry, you're right. In any case, the question that he/she asked was whether HashSets use equality or identity; it's just his/her example that's a tad flawed. – Dawood ibn Kareem Jan 23 '12 at 05:15
  • Then why is there an intern() method if they are automatically interned? – OkonX Jan 23 '12 at 05:20
  • 5
    Because they're only automatically interned if they start out as literals. But if you get the String from somewhere else (like you read it from a file, or build it up from substrings, for example), then interning it will change its identity. – Dawood ibn Kareem Jan 23 '12 at 05:24
3

Two things:

  1. A set would be pretty useless unless it called the equals() method to determine equality. wordset.contains(b) will return true because a.equals(b) == true.

  2. You cannot be totally sure that a and b are pointing to different objects. Checkout String.intern() for more details.

Chip
  • 3,226
  • 23
  • 31
2

Ultimately contains will check for equals method rather then its object id validation for contains method. Hence equals method will be called for contains call. This is the call structure of contains method.

private transient HashMap<E,Object> map;
    public boolean contains(Object o) {
    return map.containsKey(o);
    }


    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }


    final Entry<K,V> getEntry(Object key) {
        int hash = (key == null) ? 0 : hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }
Bhavik Ambani
  • 6,557
  • 14
  • 55
  • 86
0

Equality. In your example, contains() returns true, because the HashSet checks a.equals( b ).

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110