I would use a double hashed hash table to resolve conflicts. The general idea is to hash your original value, and if it does collide, make a second hash that gives the step value that you will use when going through the array to find a place to place the value. This makes good use of memory since there is no overhead for pointers and it maintains reasonable efficiency at much higher load factors than linear sensing.
Edit: if you want to change what you are doing right now, one of the options is to process inserts in clusters: save a sorted array and a separate set of new inserts. When the collection of new inserts gets too large, combine these elements into the main collection.
For the secondary collection, you have several options. You can simply use the un-sorted array and do a linear search - and just limit its size to (say) log (M), where M is the size of the main array. In this case, the overall search remains O (log N), imposes memory overhead and saves most attachments fairly quickly. When you combine a collection together, you (usually) want to sort the secondary collection, and then merge with the primary. This allows you to amortize linear merging by the number of elements in the secondary collection.
Alternatively, you can use the tree for your secondary collection. This means that newly inserted elements use additional storage for pointers, but (again), while maintaining this size, there are small restrictions on overhead.
source share