Okay, after doing that, it was so slow that I decided to recode it using the quicksort to see what sort of performance improvement I could get.
The time to run a bubble sort is proportional to the square of the number of items, and looking up each of those items in a TList is proportional to the number of items, so overall a bubble-sorted TList is an O(n^3) task. Getting the data out of the TList into a plain array (or just using a plain array) and then quicksorting the array would be more like O(n log n). In practice, with my 30,000-item dataset, it went from over a minute to about 1/5 second.
My horrible kludge:
1. The objects to be sorted need ToString() and FromString(string) methods that translate all of the object's fields into a single string (just as if you were saving the stuff to disk in human-readable format, so these routines are useful to have anyway.) Put the fields in the order you want them sorted in, and use RSet() to right-justify any integers. In other words, to sort by last name and then by first name, when your data set includes names and game scores, you would translate the records into this string array:
stringarray[0] = "SMITH JOHN 123"
stringarray[1] = "SMITHERS WAYLON 1234567"
etc.
2. Alphabetize that array with stringarray.Sort()! Wow, that was fast.
3. Clear the list (thus deleting all its objects), and re-create all the objects from the strings in the array. This means writing a parser (such as the equivalent of Perl's split() function.)
Even though that's a cumbersome (and potentially fragile) way of going about things, like I said, in my case it was a 500x speedup versus using SortList().
Here's code I'm using to do that on an existing TList of CPFile objects. Assume that the CPFile objects provide ToString() and FromString(string) methods:
'''''''''''''''''''''''''''''''''''''''''''''''''''
'
' QSortList(TList, flag)
'
' Like BlitzMax's native SortList() except faster for big lists.
' Flag = True when you want alphabetical order,
' Flag = False when you want reverse order.
'
Function QSortList ( list :TList, f :Int = True )
Local o :CPFile ' temporary object
Local b :Int ' loop counter
' first convert the list of objects to an array of objects
Local oa :Object[] = ListToArray(list) ' object array
Local sa :String[oa.length] ' string array
' convert the array of objects to an array of strings
For b = 0 To sa.length-1
sa[b] = oa[b].ToString()
Next
' sort the array
sa.Sort(f)
' rebuild the original list of CPFiles from the array of strings
ClearList(list)
For b = 0 To sa.length-1
o = New CPFile
o.FromString(sa[b])
list.AddLast(o)
Next ' b
Return
End Function ' QSortList
Basically, if you can pack your data into an array of strings rather than a TList of fancy objects, the combination of doing those conversions plus the quicksort is still a zillion times faster than sorting the TList. Linked lists aren't efficient data structures in situations where you don't need their unique features.
(And if your objects are already stored in an array rather than a TList, that saves a few steps.)
The bulk of the work here is that you need to write ToString() and FromString(string) methods for your objects, but again, if you need to save them to disk, you can use those same routines (also great for debugging, because the save files can be opened in your text editor.)
If you want to sort the same data according to different rules, then just add a "Select" block to the ToString and FromString methods akin to what I did in the previous example.