DJ180
DJ180

Reputation: 19854

Java: Retrieving an element from a HashSet

Why cannot I retrieve an element from a HashSet?

Consider my HashSet containing a list of MyHashObjects with their hashCode() and equals() methods overridden correctly. I was hoping to construct a MyHashObject myself, and set the relevant hash code properties to certain values.

I can query the HashSet to see if there "equivalent" objects in the set using the contains() method. So even though contains() returns true for the two objects, they may not be == true.

How come then there isn’t any get() method similar to how the contains() works?

What is the thinking behind this API decision?

Upvotes: 73

Views: 321889

Answers (10)

MVM
MVM

Reputation: 161

You can use HashMap<MyHashObject, MyHashObject> instead of HashSet<MyHashObject>.

Calling containsKey() on your "reconstructed" MyHashObject will first hashCode() - check the collection, and if a duplicate hashcode is hit, finally equals() - check your "reconstructed" against the original, at which you can retrieve the original using get()

Complexity is O(1) but the downside is you will likely have to override both equals() and hashCode() methods.

Upvotes: 16

andersoj
andersoj

Reputation: 22884

If you know what element you want to retrieve, then you already have the element. The only question for a Set to answer, given an element, is whether it contains() it or not.

If you want to iterator over the elements, just use a Set.iterator().

It sounds like what you're trying to do is designate a canonical element for an equivalence class of elements. You can use a Map<MyObject,MyObject> to do this. See this Stack Overflow question or this one for a discussion.

If you are really determined to find an element that .equals() your original element with the constraint that you must use the HashSet, I think you're stuck with iterating over it and checking equals() yourself. The API doesn't let you grab something by its hash code. So you could do:

MyObject findIfPresent(MyObject source, HashSet<MyObject> set)
{
   if (set.contains(source)) {
      for (MyObject obj : set) {
        if (obj.equals(source))
          return obj;
      }
   }

  return null;
}

It is brute-force and O(n) ugly, but if that's what you need to do...

Upvotes: 58

Md. Rezaul Islam
Md. Rezaul Islam

Reputation: 11

First of all, convert your set to an array. Then, get the item by indexing the array.

Set uniqueItem = new HashSet();
uniqueItem.add("0");
uniqueItem.add("1");
uniqueItem.add("0");

Object[] arrayItem = uniqueItem.toArray();
for(int i = 0; i < uniqueItem.size(); i++) {
    System.out.println("Item " + i + " " + arrayItem[i].toString());
}

Upvotes: 1

android developer
android developer

Reputation: 116392

If you want to have a reference to the real object using the same performance as HashSet, I think the best way is to use HashMap.

Example (in Kotlin, but similar in Java) of finding an object, changing some field in it if it exists, or adding it in case it doesn't exist:

val map = HashMap<DbData, DbData>()
val dbData = map[objectToFind]
if(dbData!=null){
  ++dbData.someIntField
}
else {
  map[dbData] = dbData
}

Upvotes: 0

Saravanakumar V
Saravanakumar V

Reputation: 9

If I know for sure in my application that the object is not used in search in any of the list or hash data structure and not used equals method elsewhere except the one used indirectly in hash data structure while adding. Is it advisable to update the existing object in set in equals method. Refer the below code. If I add the this bean to HashSet, I can do group aggregation on the matching object on key (id). By this way I am able to achieve aggregation functions such as sum, max, min, ... as well. If not advisable, please feel free to share me your thoughts.

public class MyBean {

    String id,
           name;
    double amountSpent;

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

    @Override
    public boolean equals(Object obj) {
        if(obj!=null && obj instanceof MyBean ) {
            MyBean tmpObj = (MyBean) obj;
            if(tmpObj.id!=null && tmpObj.id.equals(this.id)) {
                tmpObj.amountSpent += this.amountSpent;
                return true;
            }
        }
        return false;
    }
}

Upvotes: 1

Crenguta S
Crenguta S

Reputation: 512

One of the easiest ways is to convert to Array:

for(int i = 0; i < set.size(); i++) {
    System.out.println(set.toArray()[i]);
}

Upvotes: 0

V.Tran
V.Tran

Reputation: 487

The idea that you need to get the reference to the object that is contained inside a Set object is common. It can be archived by 2 ways:

  1. Use HashSet as you wanted, then:

    public Object getObjectReference(HashSet<Xobject> set, Xobject obj) {
        if (set.contains(obj)) {
            for (Xobject o : set) {
                if (obj.equals(o))
                    return o;
            }
        }
        return null;
    }
    

For this approach to work, you need to override both hashCode() and equals(Object o) methods In the worst scenario we have O(n)

  1. Second approach is to use TreeSet

    public Object getObjectReference(TreeSet<Xobject> set, Xobject obj) {
        if (set.contains(obj)) {
            return set.floor(obj);
        }
        return null;
    }
    

This approach gives O(log(n)), more efficient. You don't need to override hashCode for this approach but you have to implement Comparable interface. ( define function compareTo(Object o)).

Upvotes: 4

Habib Karbasian
Habib Karbasian

Reputation: 666

If you could use List as a data structure to store your data, instead of using Map to store the result in the value of the Map, you can use following snippet and store the result in the same object.

Here is a Node class:

private class Node {
    public int row, col, distance;

    public Node(int row, int col, int distance) {
        this.row = row;
        this.col = col;
        this.distance = distance;
    }

    public boolean equals(Object o) {
        return (o instanceof Node &&
                row == ((Node) o).row &&
                col == ((Node) o).col);
    }
}

If you store your result in distance variable and the items in the list are checked based on their coordinates, you can use the following to change the distance to a new one with the help of lastIndexOf method as long as you only need to store one element for each data:

    List<Node> nodeList;
    nodeList = new ArrayList<>(Arrays.asList(new Node(1, 2, 1), new Node(3, 4, 5)));
    Node tempNode = new Node(1, 2, 10);
    if(nodeList.contains(tempNode))
        nodeList.get(nodeList.lastIndexOf(tempNode)).distance += tempNode.distance;

It is basically reimplementing Set whose items can be accessed and changed.

Upvotes: 0

IgorGanapolsky
IgorGanapolsky

Reputation: 26821

If you know the order of elements in your Set, you can retrieve them by converting the Set to an Array. Something like this:

Set mySet = MyStorageObject.getMyStringSet();
Object[] myArr = mySet.toArray();
String value1 = myArr[0].toString();
String value2 = myArr[1].toString();

Upvotes: 6

Jon Newmuis
Jon Newmuis

Reputation: 26502

It sounds like you're essentially trying to use the hash code as a key in a map (which is what HashSets do behind the scenes). You could just do it explicitly, by declaring HashMap<Integer, MyHashObject>.

There is no get for HashSets because typically the object you would supply to the get method as a parameter is the same object you would get back.

Upvotes: 7

Related Questions