Java: Retrieving an element from a HashSet

Java

Java Problem Overview


Hoping someone can explain why I cannot retrieve an element from a HashSet.

Consider my HashSet containing a list of MyHashObjects with their hashCode() and equals() methods overridden correctly.

What I was hoping to do was 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 2 objects, they may not be == true.

How come then there is no get() method similar to how the contains() works?

Interested to know the thinking behind this API decision

Java Solutions


Solution 1 - Java

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

Brute force and O(n) ugly, but if that's what you need to do...

Solution 2 - Java

You can 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()

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

Solution 3 - Java

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.

Solution 4 - Java

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();

Solution 5 - Java

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

Solution 6 - Java

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

Solution 7 - Java

First of all convert your set to Array. Then, get item by index of 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());
}

Solution 8 - Java

One of the easiest ways is to convert to Array:

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

Solution 9 - Java

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDJ180View Question on Stackoverflow
Solution 1 - JavaandersojView Answer on Stackoverflow
Solution 2 - JavaMVMView Answer on Stackoverflow
Solution 3 - JavaJon NewmuisView Answer on Stackoverflow
Solution 4 - JavaIgorGanapolskyView Answer on Stackoverflow
Solution 5 - JavaV.TranView Answer on Stackoverflow
Solution 6 - JavaSaravanakumar VView Answer on Stackoverflow
Solution 7 - JavaMd. Rezaul IslamView Answer on Stackoverflow
Solution 8 - JavaCrenguta SView Answer on Stackoverflow
Solution 9 - JavaHabib KarbasianView Answer on Stackoverflow