Introduction
When you are writing something like a game engine, there are usually many classes there which must be organized as containers for some data. For example, you might need to store your timers, textures, sounds, and so on.
These content classes can have additional information which must be found in the game cycle as fast as possible. It is not a big problem to store your data in List<T>
containers when your have a small amount of them, but it can be a problem if you have more than 10000 objects in one container. Serial search on an unsorted list is too slow.
There are non-generic SortedList
or generic SortedDictionary
collections in the C# toolkit, but they can be sorted only by one field at a time and the dictionary syntax is not the best for most of your classes.
I’ve tried to solve this problem with a Generic class, which contains dynamically sorted and synchronized lists for any public property of the content class.
Background
This idea comes when I thought about how it works in databases. As far as I know, any index you add to a database becomes a sorted list of the field (fields for multifield indexes). And then, you can use fast searching algorithms on these fields.
So, I tried to realize the idea in the C# world ;).
You can add as many indexes as you wish, with your own aliases, sorting order, and unique flags.
Even more - you can add two indexes with deferent aliases for the same property if you need to have ascending and descending orders at the same time.
I’m using a fast binary search algorithm, which has one limitation – it is correct when using the BinarySearch()
method only for fields with the unique flag=true. This is because my class is not accepted when searching for more then one field at the same time :( (analog of “Select
” command in SQL).
All indexers are sorted by the corresponding property, lists of references to instances of the content class, so you can use them for your complex searches (function GetIndexer(string alias)
).
Unique flag is used for automation of adding only instances with unique values of the target property (for the designation of key fields).
Using the code
Indexed class T
by property ID. This is part of the IndexedList<T>
class. Supports sorting order and unique key protection.
public class ListIndex<T>
{
private string id;
public string ID
{
get { return id; }
}
private bool unique;
public bool Unique
{
get { return unique; }
}
private int direction;
public int Direction
{
get { return direction; }
}
private string alias;
public string Alias
{
get { return alias; }
}
private List<T> list;
public List<T> List
{
get { return list; }
}
public ListIndex(string id)
: this(id,id,false,false)
{
}
public ListIndex(string id,string alias,bool backDirection,bool unique)
{
}
public void Dispose()
{
}
~ListIndex()
{
Dispose();
}
}
The main class-container for the indexed list of classes T
.
public class IndexedList<T> where T:class
{
private List<T> list;
public List<T> List
{
get { return list; }
}
BindingFlags bindingAttr=BindingFlags.Public | BindingFlags.Instance;
public IndexedList()
{
}
public ListIndex<T> GetIndexer(string indexAlias)
{
}
public T BinarySearch(string indexAlias,object v)
{
return BinarySearch(GetIndexer(indexAlias),v);
}
public T BinarySearch(ListIndex<T> index,object v)
{
}
public int Search4index(ListIndex<T> index,object v,bool inserting)
{
}
public bool Add(T obj)
{
}
public void Delete(T obj)
{
}
public void Delete(string indexID,object fieldValue)
{
Delete(GetIndexer(indexID),fieldValue);
}
public bool Delete(ListIndex<T> index,object fieldValue)
{
}
public bool AddIndex(string id)
{
return AddIndex(id,id,false,false);
}
public bool AddIndex(string id,string alias,bool backDirection,bool unique)
{
}
public void DelIndex(string alias)
{
}
public void Dispose()
{
}
~IndexedList()
{
Dispose();
}
}
Points of interest
It was interesting to build such a generic class which can be used in many different situations.
The code
There is an example in the attachment showing how to:
- Add/delete indexes
- Add some data
- Get an indexer
- Using the binary search
Performance testing
This implementation is not very fast because of using the Reflection mechanism. But in any case, it is 3-4 times faster than serial search on a 10000 object list and more than 20 times faster when searching in a list with 100,000 objects.