0

I'm looking for a C# generic container that it's a List<T>, but no repeated elements allowed.

In another word, it's a Set<T>, but can also be accessed via [index] operator.

Thanks.

Peter Lee
  • 12,931
  • 11
  • 73
  • 100
  • I don't think such a thing exists. What do you want it to do when you try to duplicate an element? Throw? – sblom Aug 15 '12 at 05:28
  • Why would you want to be able to access a specific index? You can use use a key instead of an index. To enumerate all items, just use foreach. – logicnp Aug 15 '12 at 05:29
  • 2
    This may be helpful... http://stackoverflow.com/questions/2200271/unique-listt-in-net-2 – Glen Hughes Aug 15 '12 at 05:30
  • 1
    There is a ElementAt extension method for HashSet . You will need System.Linq – dan_l Aug 15 '12 at 05:32
  • 2
    Why not encapsulate both types of collection in a new class, and ensure that the add/remove methods maintain both collections? What problem are you trying to solve? I rarely need indexes into my collections. – spender Aug 15 '12 at 05:33
  • I also though about `ElementAt`, but does the it guarantee a reasonable index order? – Peter Lee Aug 15 '12 at 05:33
  • ....no it doesn't. It picks the element in position x in an IEnumerable of indeterminate order. – spender Aug 15 '12 at 05:36
  • 1
    ... and that order can change. Basically, using ElementAt on a set is a really bad idea. – Jon Skeet Aug 15 '12 at 05:44
  • @PeterLee What is a "reasonable index order" ? I guess you want insertion order. – dan_l Aug 15 '12 at 06:46

3 Answers3

1

As suggested in the comments, you could implement IList<T> that delegates to an inner List<T> instance, and guards add and remove calls with using a HashSet<T>:

public class UniqueList<T> : IList<T>
{
    private readonly List<T> list=new List<T>();
    private readonly HashSet<T> set=new HashSet<T>();
    public IEnumerator<T> GetEnumerator()
    {
        return list.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void Add(T item)
    {
        if(set.Add(item))
        {
            list.Add(item);
        }
    }

    public void Clear()
    {
        set.Clear();
        list.Clear();
    }

    public bool Contains(T item)
    {
        return set.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        list.CopyTo(array,arrayIndex);
    }

    public bool Remove(T item)
    {
        if(set.Remove(item))
        {
           list.Remove(item);
            return true;
        }
        return false;
    }

    public int Count { get { return list.Count; } }
    public bool IsReadOnly { get { return false; } }
    public int IndexOf(T item)
    {
        return list.IndexOf(item);
    }

    public void Insert(int index, T item)
    {
        if(set.Add(item))
        {
            list.Insert(index, item);
        }
    }

    public void RemoveAt(int index)
    {
        T item = list[index];
        set.Remove(item);
        list.RemoveAt(index);
    }

    public T this[int index]
    {
        get { return list[index]; }
        set {
            T item = list[index];
            set.Remove(item);
            if(set.Add(value))
            {
                list[index] = value;    
            }
            else
            {
                set.Add(item);
                throw new Exception();
            }


        }
    }
}

I didn't compile this code, but you get the idea...

spender
  • 117,338
  • 33
  • 229
  • 351
1

No such thing is provided in the framework (and HashSet<T> does not guarantee any specific order, so you can't cheat with ElementAt). The closest inbuilt you could use would be something like SortedList<T,anything> (the "anything" there doesn't matter, and could be T, int, whatever), for example:

var data = new SortedList<string, int>();
data["abc"] = 1;
data["def"] = 1;
data["abc"] = 1;
var thisIsTrue = data.ContainsKey("def");
var thisIsFalse = data.ContainsKey("ghi");
for (int i = 0; i < data.Count; i++) // 2 iterations
    Console.WriteLine(data.Keys[i]); // abc, def

However; it is important to note that the guaranteed order here is key order, not insertion order. The index of keys is available via data.IndexOfKey.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
0

You could use the OrderedDictionary. If you use your type for both key and value type, and use the same object as both key and value, you get the behavior you need (You could also use the keys collection for index based retrieval; and stick dummy data into the value, but I'm not sure what you would gain).

Menno van den Heuvel
  • 1,851
  • 13
  • 24