Paolo Fulgoni
Paolo Fulgoni

Reputation: 5498

Check if a collection contains an object, comparing by reference

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:

Edit:

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

Upvotes: 29

Views: 48899

Answers (6)

arshajii
arshajii

Reputation: 129507

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

collection.stream().anyMatch(x -> x == key)

Upvotes: 43

OldCurmudgeon
OldCurmudgeon

Reputation: 65811

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;
    }

}

Upvotes: 0

Paolo Fulgoni
Paolo Fulgoni

Reputation: 5498

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.

Upvotes: 2

fge
fge

Reputation: 121710

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.

Upvotes: 11

Suresh Atta
Suresh Atta

Reputation: 121998

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).

Upvotes: 3

Germann Arlington
Germann Arlington

Reputation: 3353

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.

Upvotes: 2

Related Questions