Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting an element from a Set

Why doesn't Set provide an operation to get an element that equals another element?

Set<Foo> set = ...; ... Foo foo = new Foo(1, 2, 3); Foo bar = set.get(foo);   // get the Foo element from the Set that equals foo 

I can ask whether the Set contains an element equal to bar, so why can't I get that element? :(

To clarify, the equals method is overridden, but it only checks one of the fields, not all. So two Foo objects that are considered equal can actually have different values, that's why I can't just use foo.

like image 915
foobar Avatar asked Sep 02 '11 12:09

foobar


People also ask

How do I get an element from a set in Python?

You cannot access items in a set by referring to an index or a key. But you can loop through the set items using a for loop, or ask if a specified value is present in a set, by using the in keyword.

Can we get index from set?

Just go through the set using iterator and counter, check the object for equality. If found, return the counter. TreeSet will not keep the order in which the elements were added, they will be ordered in their natural order.


2 Answers

To answer the precise question "Why doesn't Set provide an operation to get an element that equals another element?", the answer would be: because the designers of the collection framework were not very forward looking. They didn't anticipate your very legitimate use case, naively tried to "model the mathematical set abstraction" (from the javadoc) and simply forgot to add the useful get() method.

Now to the implied question "how do you get the element then": I think the best solution is to use a Map<E,E> instead of a Set<E>, to map the elements to themselves. In that way, you can efficiently retrieve an element from the "set", because the get() method of the Map will find the element using an efficient hash table or tree algorithm. If you wanted, you could write your own implementation of Set that offers the additional get() method, encapsulating the Map.

The following answers are in my opinion bad or wrong:

"You don't need to get the element, because you already have an equal object": the assertion is wrong, as you already showed in the question. Two objects that are equal still can have different state that is not relevant to the object equality. The goal is to get access to this state of the element contained in the Set, not the state of the object used as a "query".

"You have no other option but to use the iterator": that is a linear search over a collection which is totally inefficient for large sets (ironically, internally the Set is organized as hash map or tree that could be queried efficiently). Don't do it! I have seen severe performance problems in real-life systems by using that approach. In my opinion what is terrible about the missing get() method is not so much that it is a bit cumbersome to work around it, but that most programmers will use the linear search approach without thinking of the implications.

like image 90
jschreiner Avatar answered Oct 02 '22 13:10

jschreiner


There would be no point of getting the element if it is equal. A Map is better suited for this usecase.


If you still want to find the element you have no other option but to use the iterator:

public static void main(String[] args) {      Set<Foo> set = new HashSet<Foo>();     set.add(new Foo("Hello"));      for (Iterator<Foo> it = set.iterator(); it.hasNext(); ) {         Foo f = it.next();         if (f.equals(new Foo("Hello")))             System.out.println("foo found");     } }  static class Foo {     String string;     Foo(String string) {         this.string = string;     }     @Override     public int hashCode() {          return string.hashCode();      }     @Override     public boolean equals(Object obj) {         return string.equals(((Foo) obj).string);     } } 
like image 33
dacwe Avatar answered Oct 02 '22 13:10

dacwe