My god what was I thinking, using StringCollection over ArrayList?

I don't know why I figured the StringCollection might actually be more performant than the ArrayList.  I guess I assumed this was the one case where they would emit a strongly typed collection that was based on ArrayList, but was strongly typed on the back-end.  However, looking at the rotor source, all it does is inherit from ArrayList and really doesn't provide any added value (except that you don't have to do the string cast when pulling elements out).

Doing a basic timed iteration over a StringCollection using a large number of elements (in the millions), it was almost twice as fast to just use the ArrayList and forget about StringCollection altogether.  Unless you want a collection that is easy to use from the standpoint of not having to do the string casts, I would highly recommend against the StringCollection.  It just doesn't buy you anything.  I would highly recommend using the ArrayList source code from the rotor code-base to write your own strongly typed collection though.  You have to remember how much time went into setting the initialCapacity and determining the growth metrics for the ArrayList thanks to the performance team at MS.  They made the ArrayList one of the best collections available.

I've heard of some decent tools for developing strongly typed collections as well.  CodeSmith comes to mind, so you might want to check those out.  All of these things become null and void (for the most part with some exceptions) when generics hit the shelves since generics make it easy to write strongly typed collections and most of the collections classes/interfaces have already undergone a generics overhaul and are quite performant.  If you'd like to run the numbers for yourself of ArrayList versus StringCollection you can just compile the below test class.  Enjoy.

using System;
using System.Collections;
using System.Collections.Specialized;

public class StringCollectionTest {
    private static void Main(string[] args) {
        DateTime start, end;
        int iterations = int.Parse(args[0]);

        /*
            Get all the strings into a strongly typed
            array so we don't incur the hit of placing
            them into the string table during the actual
            usage of the collections.
        */
        string[] strings = new string[iterations];
        for(int i = 0; i < strings.Length; i++) {
            strings[i] = i.ToString();
        }

        /*
            Create a string collection and add all of
            the strings.  Note this is just adding the
            strings to an ArrayList underneath.
           
            Once done, do a CopyTo our newly allocated
            strongly typed string[].
        */
        StringCollection coll = new StringCollection();
        start = DateTime.Now;
        for(int i = 0; i < strings.Length; i++) {
            coll.Add(strings[i]);
        }
        string[] collStrings = new string[strings.Length];
        coll.CopyTo(collStrings, 0);
        end = DateTime.Now;
        Console.WriteLine("String Collection: {0}", end - start);


        /*
            Create an ArrayList.  Add all of the strings
            to the collection.
           
            Once done, do a ToArray, which internally creates
            a newly allocated array of the appropriate length
            and type and returns it after copying all elements
            into place.
        */
        ArrayList coll2 = new ArrayList();
        start = DateTime.Now;
        for(int i = 0; i < strings.Length; i++) {
            coll2.Add(strings[i]);
        }
        string[] coll2Strings = (string[]) coll2.ToArray(typeof(string));
        end = DateTime.Now;
        Console.WriteLine("ArrayList        : {0}", end - start);
    }
}

Published Sunday, February 29, 2004 12:56 AM by Justin Rogers

Comments

Sunday, February 29, 2004 4:15 AM by Kartal Guner

# re: My god what was I thinking, using StringCollection over ArrayList?

I assumed the same thing. Good to know this though. I haven't gotten to the point where I am trying to fine tune the performance yet. The StringCollection has a Contains function which is nice though.
Sunday, February 29, 2004 5:36 AM by Justin Rogers

# re: My god what was I thinking, using StringCollection over ArrayList?

Contains is part of IList, and so ArrayList also has a Contains method. It uses an Equals override to determine equality.

if (item.Equals(_items[i]))

For strings, Equals is an internal call. It looks something like the following code which is a basic C++ string comparision function, with the exception of there being a couple of additional items to handle managed specifics.

//
//
// COMPARATORS
//
//
bool WcharCompareHelper (STRINGREF thisStr, STRINGREF valueStr)
{
DWORD *thisChars, *valueChars;
int thisLength, valueLength;

//Get all of our required data.
RefInterpretGetStringValuesDangerousForGC(thisStr, (WCHAR**)&thisChars, &thisLength);
RefInterpretGetStringValuesDangerousForGC(valueStr, (WCHAR**)&valueChars, &valueLength);

//If they're different lengths, they're not an exact match.
if (thisLength!=valueLength) {
return false;
}

// Loop comparing a DWORD (2 WCHARs) at a time.
while ((thisLength -= 2) >= 0)
{
if (*thisChars != *valueChars)
return false;
++thisChars;
++valueChars;
}

// Handle an extra WCHAR.
if (thisLength == -1)
return (*((WCHAR *) thisChars) == *((WCHAR *) valueChars));

return true;
}

Leave a Comment

(required) 
(required) 
(optional)
(required)