29

The Collection.contains() method check if a collection contains a given object, using the .equals() method to perform the comparison.

From Java7 Javadoc:

boolean contains(Object o)

Returns true if this collection contains the specified element. More formally, returns true if and only if this collection contains at least one element e such that (o==null ? e==null : o.equals(e)).

Is there a smart way to check if a collection contains an object o, but comparing by reference instead (i.e. o==e)?

Of course I can iterate through the collection and make the check, I'm looking for an existing function which can do that.

Clarifications:

  • I want to perform this operation regardless the equals() implementation of the object in the collection.
  • I don't want to change the objects in the collection nor the collection itself.

Edit:

Even though my question is about a general solution for Collection implementations, specific cases for Collection sub-interfaces would also be appreciated.

Paolo Fulgoni
  • 5,208
  • 3
  • 39
  • 55
  • I wonder if a solution using the [exists method from apache commons collections utilities](http://commons.apache.org/proper/commons-collections/javadocs/api-3.2.1/org/apache/commons/collections/CollectionUtils.html) could work? Define a Predicate that is an instance check for the object you're trying to find. – Dan Temple Mar 21 '14 at 11:24
  • "The Collection.contains() method check if a collection contains a given object, using the .equals() method to perform the comparison" <-- the documentation is rather misleading here; this is not true for all collection types. For instance, `Sorted*` collections rely on natural ordering. And `IdentityHashMap` relies on... Reference equality (!) for keys. – fge Mar 21 '14 at 11:33
  • @Paolo In this case you need to override equals() and hashCode() to achieve your requirement. Also suggested in one of answers below. – Sukane Mar 21 '14 at 11:41
  • Can you post something which you have tried? – Sukane Mar 21 '14 at 11:43
  • @Exu do you mean code which shows the problem or code which tries to solve the issue? First one, I think it is not needed. Last one, see [answer below](http://stackoverflow.com/a/22560840/323447) – Paolo Fulgoni Mar 21 '14 at 14:45

6 Answers6

43

For those of us using Java 8, Collection#stream() is a clean option:

collection.stream().anyMatch(x -> x == key)
Paolo Fulgoni
  • 5,208
  • 3
  • 39
  • 55
arshajii
  • 127,459
  • 24
  • 238
  • 287
11

There is some kind of workaround...

You can use an IdentityHashMap, with Void as values (or whatever else -- your choice). You'd then use contains() on its .keySet() to check the presence of an element (or .containsKey() on the map directly).

A second workaround would be to use Guava and Equivalence.identity(); however your Collection will have to have elements of type Equivalence.Wrapper<X> and you'd have to wrap before checking...


Curiously enough, the JDK does not provide an IdentityHashSet. This is rather strange considering that the internal implementation of HashSet uses a HashMap, so one has to wonder why they didn't do the same for IdentityHashMap...


Side note: the documentation of Collection is misleading; not all Collection implementations rely on .equals(). See, for instance, SortedSet or SortedMap. And, of course, IdentityHashMap.

fge
  • 119,121
  • 33
  • 254
  • 329
  • 2
    There's [`Collections.newSetFromMap`](http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#newSetFromMap(java.util.Map)) if you need an IdentityHashSet. – user2357112 Mar 21 '14 at 19:20
  • @user2357112 uh, I had forgotten about that one! Probably because I have never used it ;) – fge Mar 21 '14 at 19:21
3

There is no way to check that the way you are trying to do. Without iterating through collection you cannot check that the object point to same reference or not.

AFAIK, No (At least a clean way).

Suresh Atta
  • 120,458
  • 37
  • 198
  • 307
2

When you create a class you should override at least equals (and hashCode) methods.
If you implement your equals method to compare by reference you will achieve your goal.

Germann Arlington
  • 3,315
  • 2
  • 17
  • 19
  • This does not answer the question at all – fge Mar 21 '14 at 11:14
  • 1
    I want to perform that operation regardless the `equals` implementation. Added as clarification in my question. – Paolo Fulgoni Mar 21 '14 at 11:19
  • 3
    @fge The OP wanted to check if Collection contains a given object by reference. Why would overriding object's `equals` method to compare references not be an answer? – Germann Arlington Mar 21 '14 at 11:24
  • In that case you will have to implement your own Collection – Germann Arlington Mar 21 '14 at 11:26
  • Because OP said explicitly that this should work regardless of the class' `.equals()` and `.hashCode()` implementations... And the JDK does have a collection implementation of it (sorta) with `IdentityHashMap` – fge Mar 21 '14 at 11:28
  • 1
    @fge This was a later clarification by OP if you care to read the post and comments... The answer was a solution for original question. – Germann Arlington Mar 21 '14 at 11:37
  • No. The original question clearly asked "[...]Is there a smart way to check if a collection contains an object o, but comparing by reference instead (i.e. o==e)?". It is only because of all answers mentioning `.equals()`/`.hashCode()` that the OP felt obliged to edit for clarification. – fge Mar 21 '14 at 11:38
  • @fge Overriding `equals` method to compare references would handle the requirement automatically because Collection (Map is **not** a strict Collection unless you will consider it as a Collection of Entry elements in which case it follows the same patten) will have to check object `equals` to decide if the entry is present. – Germann Arlington Mar 21 '14 at 11:53
  • Not all `Collection`s rely on `.equals()`!! See `TreeSet`, which implements `Collection`... Also, why would you want to override `.equals()` since `Object`'s one does identity comparison? – fge Mar 21 '14 at 11:59
  • @fge Who guaranteed that the object you will be storing extends `Object` directly and not another class with possible alternative implementation of `equals` As for TreeSet it uses HashMap to enforce `unique` clause of the Set – Germann Arlington Mar 21 '14 at 12:25
  • You still don't get it, do you? Here what is wanted is _reference equality_. And this is where `IdentityHashMap` comes in. As for `TreeSet` it DOES NOT use `.equals()` to enforce uniqueness. Try and insert `new BigDecimal("1.0")` and `BigDecimal.ONE` into a `TreeSet`: those two are not `.equals()`, but the resulting set only contains ONE element. This is because their `.compareTo()` returns 0. – fge Mar 21 '14 at 12:29
  • Yes, I don't get it: how does overriding `... equals(Object o) { return this == o; }` not produce the desired outcome? – Germann Arlington Mar 21 '14 at 13:15
  • Because the OP wants it to work _regardless of the class' implementation of it_ (ie, without having to override it at all) – fge Mar 21 '14 at 13:22
  • @fge - as far as I understand it TreeSet implementation is in breach of contract between `equals()` and `compareTo()` – Germann Arlington Mar 21 '14 at 13:43
  • Which is why I said that the documentation of `Collection` is misleading... The `SortedSet` interface "violates" `Collection`'s definition of `.contains()`, not `TreeSet` alone (`TreeMap` also behaves this way) – fge Mar 21 '14 at 13:45
  • @fge - as I mentioned earlier OP clarified that they did not want class dependent implementation later. – Germann Arlington Mar 21 '14 at 13:46
  • @fge - Tree alrrady implies the sorting, and once again Map is not a stict Collection – Germann Arlington Mar 21 '14 at 13:48
  • A `Map`, no; its `.keySet()`, yes ;) – fge Mar 21 '14 at 13:56
2

The answers point out that it is not possible in a clean way to perform the desired check.

So this is a possible implementation of such requested function:

/**
 * Returns {@code true} if the collection contains the specified element.
 * <p>
 * More formally, returns {@code true} if and only if this collection
 * contains at least one element {@code x} such that {@code x == element}.
 * <p>
 * Note: {@link Collection#contains(Object)} works differently because uses
 * {@link Object#equals(Object)} for comparison
 * 
 * @param collection
 *            collection where to look for the element
 * @param element
 *            element whose presence in this collection is to be tested
 * @return {@code true} if this collection contains the specified element
 * @throws NullPointerException
 *             if {@code collection} is null
 */
public static <T> boolean containsReferenceTo(Collection<T> collection,
        T element) {
    if (collection == null)
        throw new NullPointerException("collection cannot be null");

    for (T x : collection) {
        if (x == element) {
            return true;
        }
    }
    return false;
}

NOTE: this may be optimized for some specific Collection implementations.

Paolo Fulgoni
  • 5,208
  • 3
  • 39
  • 55
  • 3
    Note: with Java 7 the null check can be written `Objects.requireNonNull(collection, "collection cannot be null");` – fge Mar 21 '14 at 16:08
  • can anyone optimize the function in case `collection` is an instance of `IdentityHashMap`? – Paolo Fulgoni Mar 21 '14 at 16:49
  • 1
    Well, it won't be an instance of `IdentityHashMap` since `Map` does not implement `Collection`... It is its `.keySet()` which does; and I don't believe there is a way to detect that – fge Mar 21 '14 at 17:05
0

You should be able to do it by wrapping the object in your own object which implements the equals you are looking for.

Something like:

private class Identical<T> {
    final T held;

    Identical (T hold) {
        held = hold;
    }

    public boolean equals(Object it) {
        return it != null && held == it;
    }
}

Obviously you would have to take control of adding items to the collection and wrap each one in one of these.

You could wrap the map like this:

static class ReferenceHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {

    private final Map<K, Identical<V>> map = new HashMap<>();

    private static class Identical<T> {

        final T held;

        Identical(T hold) {
            held = hold;
        }

        @Override
        public boolean equals(Object it) {
            return it != null && held == it;
        }

        @Override
        public int hashCode() {
            return held.hashCode();
        }
    }

    @Override
    public V get(Object key) {
        Identical<V> value = map.get(key);
        return value == null ? null : value.held;
    }

    @Override
    public V put(K key, V value) {
        Identical<V> replaced = map.put(key, new Identical<>(value));
        return replaced == null ? null : replaced.held;
    }

    private class MyEntry implements Map.Entry<K, V> {

        private final K key;
        private V value;

        public MyEntry(K key, V value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        @Override
        public V setValue(V value) {
            V old = this.value;
            this.value = value;
            return old;
        }
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        Set<Entry<K, V>> entries = new HashSet<>();
        for (Entry<K, Identical<V>> entry : map.entrySet()) {
            entries.add(new MyEntry(entry.getKey(), entry.getValue().held));
        }
        return entries;
    }

}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • wouldn't you want to replace the keys with `Identical`, instead of the values? also, in your first block of code, the `hashCode()` method is missing. – Silly Freak Mar 21 '14 at 22:16