Accessing the item at a specified index in a 'SortedSet'
EDIT: An ordinary (unordered) set such as HashSet<T> manages its elements in no particular order. So, the index of a particular element in an unordered set does not carry any particular meaning.
In contrast however, it makes semantic sense to request an element by its position (index) in a SortedSet<T>. Why bother with the overhead of an ordered collection, otherwise?
That said, for a small SortedSet<T> where performance is not a concern (see example below), the Linq extension method Enumerable.ElementAt() provides a convenient means of retrieving an item by its index. However, for a large SortedSet<T> where the runtime performance of retrieving an element is paramount, consider implementing a custom collection as @Nicholas Carey outlines in his answer.
Original Answer:
You can access an item of interest by its index (position) from your SortedSet
via the Enumerable.ElementAt<TSource>
method:
var item = mySortedSet.ElementAt(index);
Demonstration:
using System;
using System.Collections.Generic;
using System.Linq;
class SortedSetDemo
{
static void Main(string[] args)
{
var words = new string[]
{"the", "quick", "brown", "fox", "jumps",
"over", "the", "lazy", "dog"};
// Create a sorted set.
var wordSet = new SortedSet<string>();
foreach (string word in words)
{
wordSet.Add(word);
}
// List the members of the sorted set.
Console.WriteLine("Set items in sorted order:");
int i = 0;
foreach (string word in wordSet)
{
Console.WriteLine("{0}. {1}", i++, word);
}
// Access an item at a specified index (position).
int index = 6;
var member = wordSet.ElementAt(index);
Console.WriteLine("\nThe item at index {0} is '{1}'!", index,
member);
}
}
Expected Output:
The set items in sorted order is:
0. brown
1. dog
2. fox
3. jumps
4. lazy
5. over
6. quick
7. the
The item at position 6 is 'quick'!
That's because a SortedSet
has the semantics of a set and is not a List
-like construct. Consequently, it does not implement IList
(which give you the ability to address items by index via the Item
property).
As noted by @DavidRR, you could use the Linq extension method Enumerable.ElementAt()
. However, since the backing store of a SortedSet
is a red-black tree -- a height-balanced binary tree, accessing an element by index via ElementAt()
involves a tree walk — O(N), worst case and O(N/2) on the average, to get to the desired item. Pretty much the same as traversing a singly-linked list to access the Nth item.
So...for large sets, performance is likely to be poor.
If what you want is a unique collection that offers array-like semantics, why not roll your own IList<T>
implementation that would enforce uniqueness, just as SorteSet<T>
does (ignoring adds of elements that already exist in the colleciton). Use a List<T>
as the backing store. Maintain it in sorted sequence so you can use a binary search to determine if the element being added already exists. Or, simply subtype List<T>
and override the appropriate methods to get the semantics you want.