Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I retrieve an item from a HashSet without enumeration?

Tags:

java

c#

hashset

I'm looking for insight into the heads of HashSet designers. As far as I am aware, my question applies to both Java and C# HashSets, making me think there must be some good reason for it, though I can't think of any myself.

After I have inserted an item into a HashSet, why is it impossible to retrieve that item without enumeration, hardly an efficient operation? Especially since a HashSet is explicitly built in a way which supports efficient retrieval.

It would often be useful to me to have Remove(x) and Contains(x) return the actual item that is being removed or contained. This is not necessarily the item I pass into the Remove(x) or Contains(x) function. Sure, I guess I could achieve the same effect through a HashMap but why waste all that space and effort when it should be perfectly possible to do this with a set?

I can appreciate that there may be some design concerns that adding this functionality would allows uses of HashSet which are not consistent with their role or future role in the framework, but if this is so, what are these design concerns?

Edit

To answer some more questions, here are more details:

I am using an immutable reference type with overridden hashcode, equals, etc to emulate a value type in C#. Let's say the type has members A, B, and C. Hashcode, equals, etc depend only on A and B. Given some A and B I want to be able to retrieve that equivalent item from a hashset and get it's C. I won't be able to use HashSet for this it appears, but I would at least like to know if there is any good reason for this. Pseudo code follows:

public sealed class X{  object A;  object B;  object extra;   public int HashCode(){   return A.hashCode() + B.hashCode();  }   public bool Equals(X obj){   return obj.A == A && obj.B == B;  } }  hashset.insert(new X(1,2, extra1)); hashset.contains(new X(1,2)); //returns true, but I can't retrieve extra 
like image 972
sooniln Avatar asked Sep 29 '09 20:09

sooniln


People also ask

Why there is no get method in HashSet?

HashSet can not guarantee insertion order so no point in get method. What are you missing is implementing equals and use contains() which will iterate and find the object.

Is there a GET method in HashSet?

HashSet does not have a get method to retrieve elements. HashSet implements the Set interface. The Set is a collection with no duplicates. This interface models the mathematical set abstraction.

Why HashSet is faster than list?

The result clearly shows that the HashSet provides faster lookup for the element than the List. This is because of no duplicate data in the HashSet. The HashSet maintains the Hash for each item in it and arranges these in separate buckets containing hash for each character of item stored in HashSet.


2 Answers

In .Net, what you are probably looking for is KeyedCollection http://msdn.microsoft.com/en-us/library/ms132438.aspx

You can get around the nastiness of re-implementing this abstract class each time with some "generic" cleverness. (See IKeyedObject`1.)

Note: Any data transfer object which implements IKeyedObject`1 should have an overridden GetHashCode method simply returning this.Key.GetHashCode(); and same goes for equals...

My Base Class Library usually ends up with something like this in it:

public class KeyedCollection<TItem> : System.Collections.ObjectModel.KeyedCollection<TItem, TItem>     where TItem : class {     public KeyedCollection() : base()     {     }      public KeyedCollection(IEqualityComparer<TItem> comparer) : base(comparer)     {     }      protected override TItem GetKeyForItem(TItem item)     {         return item;     } }  public class KeyedObjectCollection<TKey, TItem> : System.Collections.ObjectModel.KeyedCollection<TKey, TItem>     where TItem : class, IKeyedObject<TKey>     where TKey : struct {     public KeyedCollection() : base()     {     }      protected override TItem GetKeyForItem(TItem item)     {         return item.Key;     } }  ///<summary> /// I almost always implement this explicitly so the only /// classes that have access without some rigmarole /// are generic collections built to be aware that an object /// is keyed. ///</summary> public interface IKeyedObject<TKey> {     TKey Key { get; } } 
like image 67
leat Avatar answered Sep 17 '22 03:09

leat


How were you proposing to retrieve the item from the hash set? A set is by definition not ordered in any way and therefore, there is no index with which to use to retrieve the object in question.

Sets, as a concept, are used to test inclusion, i.e. whether or not the element in question is in the hash data set. If you're looking to retrieve a value from a data source using a key value or index, I would suggest looking into either a Map or a List.

EDIT: Additional answer based on the Edit to the original question

Soonil, based on your new information, it looks like you might be interested in implementing your data as a Java Enum, something similar to this:

 public enum SoonilsDataType {       A, B, C;        // Just an example of what's possible       public static SoonilsDataType getCompositeValue(SoonilsDataType item1,            SoonilsDataType item2) {            if (item1.equals(A) &&                       item2.equals(B)) {                 return C;            }       }  } 

Enum's automatically inherit values() which returns the list of all values in the enum's "set", which you can use to test inclusion against in the same way as the Set. Also, because its a full class, you can define new static methods to do the composite logic (like I was trying to allude to in the example code). The only thing about the Enum is that you can't add new instances at runtime, which may not be what you want (though if the set's data size isn't going to grow at runtime, the Enum is what you want).

like image 20
Peter Avatar answered Sep 18 '22 03:09

Peter