The SLAR on System.Collections.ArrayList


Continuing in the series on sharing some of the information in the .NET Framework Standard Library Annotated Reference Vol 1 here are some of the annotations on the System.ArrayList class.


 


 


JM The IndexOf(string, int, int) method used to have as its last parameter the name “lastIndex.” This name caused confusion regarding whether the index was included in the search or not (inclusive or exclusive). Thus, as part of the standardization process the name got changed to “count” to avoid that confusion.


 


BG Note the heavy use of wrapper collections on ArrayList to add in functionality like thread safety or making a list read-only. This is very useful in that it allows users to write their code using ArrayList and then add in thread safety without having to change more than one line of code. At least, that was the original goal. However, there are two drawbacks with this approach. One is a subtle performance penalty — every method call on ArrayList is virtual, meaning it cannot be inlined and is therefore slower than it could be. The second is that the design isn’t quite what people need, at least for synchronization. Sometimes users may want code that does two operations with an ArrayList in a consistent state, like this:


ArrayList list = ArrayList.Synchronized(someOtherArrayList);


if (list.Count > 0)


   Object node = list[0];


At this point, our threadsafe wrapper will not work as expected. True, the Count property will be accessed in a thread-safe way, as will the first element of the ArrayList. However, the way our design for the threadsafe wrapper works requires that we take a lock when calling each method, then release the lock after calling each method. Therefore, after we have retrieved the Count property, another thread could add or remove an element from the list, meaning that the next line might fail. The correct way to write code like this would be to use the SyncRoot property:


lock(list.SyncRoot) {


   if (list.Count > 0)


      Object node = list[0];


}


This code is correct and has the added benefit of taking the lock only once, instead of twice. Note the thread-safe wrapper collections are expected to use the SyncRoot property (defined on ICollection) to implement their thread-safety guarantees, so doing this in one part of your code is fully compatible with using a thread-safe wrapper in a different section of your code.


 


For these reasons, the SyncRoot property is a great idea and the idea of a thread-safe wrapper is less appealing than you’d like to believe. If we were designing collections over again, we might not allow subclasses of our collections for performance reasons and because they don’t add a lot of value. The IList interface should be rich enough for most users, and if it isn’t, the correct approach is to write another interface. Classes implementing that interface are free to use ArrayList internally for their own implementation details if necessary.


 


 


And here is a sample application:


 


 


using System;


using System.Collections;


 


namespace Samples


{


  public class ArrayListConstructor


  {


    public static void Main()


    {


      string[] s = {“damien”, “mark”, “brad”};


      ArrayList a = new ArrayList(s);


      Console.WriteLine(“Count: {0}”, a.Count);


      Console.WriteLine(“Default Capacity: {0}”, a.Capacity);


      PrintElements(a);


      s[0] = “maire”;


      s[1] = “sacha”;


      s[2] = “tamara”;


      PrintElements(a);


    }


    public static void PrintElements(IEnumerable ie)


    {


      IEnumerator e = ie.GetEnumerator();


      while(e.MoveNext())


        Console.Write(“{0} “, e.Current );


      Console.WriteLine();


    }


  }


}


 


The output is


Count: 3


Default Capacity: 3


damien mark brad


damien mark brad


 


—————


 


Amazon.com Sales Rank: 7,992 (up 3676 from last post)

Comments (5)

  1. Doug McClean says:

    Subclassing collections like arraylist is pretty useful, not for thread-safety, but as the easiest way to make sure that your publicly exposed collection-typed properties don’t violate encapsulation. Generics will eliminate a lot of the need for this though.

  2. Brian Grunkemeyer says:

    We have the Collection class to use as a base class for use in object models. In the generics space, we’ll have Collection<T> for the same purpose, and none of the methods on List<T> will be virtual. This will help us a lot perf-wise, as more of the methods on List<T> as the JIT will be able to inline some method calls.

  3. Gia says:

    "If we were designing collections over again, we might not allow subclasses of our collections for performance reasons and because they don’t add a lot of value. The IList interface should be rich enough for most users, and if it isn’t, the correct approach is to write another interface. Classes implementing that interface are free to use ArrayList internally for their own implementation details if necessary."

    This seems to conflict with the suggestion that "classes are more versionable than interfaces", since one might add argue that collection interfaces like IList with 15 public methods/properties are not narrow enough.

  4. RichB says:

    virtual methods don’t have to suffer a performance penalty – the Java hotspot compiler proves that by using monomorphic inlining. It’s a shame the CLR team haven’t implemented it.

  5. Jan Gray says:

    Yes, it is certainly possible to inline selected virtual method bodies, using techniques that go back at least to Craig Chambers et al’s SELF work in the late ’80s.

    I expect the CLR to perform these kinds of optimizations in time. But at the moment we (CLR Perf team) are more focused on working set and private pages reductions, in order to minimize the marginal per-process costs of managed code in the coming Windows Longhorn era [of ubiquitous managed code].