26

This could possibly appear to be a nasty thing to ask but why do we have so short limit of number of objects in a list.

i wrote following code to test list size in C#

    List<int> test = new List<int>();            
    long test1 = 0;
    try
    {
        while (true)
        {
            test.Add(1);
            test1++;
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(test1 + "   |   " + ex.Message);
    }

and the size of list could only be 134217728

isn't that unfair :( ??? what is alternate way if i want to add objects even beyond 'integer' limits (i mean number of objects > 2^32) ???

Umer
  • 1,891
  • 5
  • 31
  • 43

4 Answers4

66

A List<int> is backed by an int[]. You will fail as soon as a larger backing array cannot be allocated - and bear in mind that:

  • There's a 2GB per-object limit in the CLR even in 64 bits (EDIT: as of .NET 4.5, this can be avoided for the 64-bit CLR - see <gcAllowVeryLargeObjects>)
  • The list will try to allocate a backing array which is larger than what it immediately requires, in order to accommodate later Add requests without reallocation.
  • During the reallocation, there has to be enough total memory for both the old and the new arrays.

Setting the Capacity to a value which will put the backing array near the theoretical limit may get you a higher cutoff point than the natural growth, but that limit will certainly come.

I would expect a limit of around 229 elements (536,870,912) - I'm slightly surprised you haven't managed to get beyond 134,217,728. How much memory do you actually have? What version of .NET are you using, and on what architecture? (It's possible that the per-object limit is 1GB for a 32-bit CLR, I can't remember for sure.)

Note that even if the per-object limit wasn't a problem, as soon as you got above 231 elements you'd have problems addressing those elements directly with List<T>, as the indexer takes an int value.

Basically, if you want a collection with more than int.MaxValue elements, you'll need to write your own, probably using multiple backing arrays. You might want to explicitly prohibit removals and arbitrary insertions :)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • List is backed by int[]? implies list is not a list, its an array and addition and deletion in array is quite costly than list, theoratically. Am i right? (assuming 'allocated backed array' might be some multiple of current list size to avoid too many allocation while 'Add') Also, with said that, what is the difference between List and int[], i'll love to read if you can share some detailed inside notes. – Umer Oct 25 '11 at 07:02
  • 6
    @Umer: The docs make it reasonably clear: "The `List` class is the generic equivalent of the `ArrayList` class. It implements the `IList` generic interface using an array whose size is dynamically increased as required." When you say it's "not a list" - that depends on what you mean by "a list". It's not a *linked* list - if you want one of those, you want to use `LinkedList`. The primary obvious difference between `List` and an array is that an array always has a fixed size, whereas a `List` can grow and shrink. – Jon Skeet Oct 25 '11 at 08:35
  • 2
    That growing and shrinking has to involve reallocation appropriately, but from an API point of view, you're still dealing with the same `List`. – Jon Skeet Oct 25 '11 at 08:36
  • 6
    @JonSkeet: You may find it informative that I tried on 64bit and hit 268435456, but hit only 134217728 on 32bit. So I think that explains part of why it stopped for Umer sooner than you expected. The second hint is in the source code of List, which tells us that if you add items 1 at a time, the capacity will always be a power of two. Thus, inserting the 134217729th item will force the capacity to be 2^28, thus requiring 2^30 memory the the new collection and 2^29 memory for still living old collection. I leave it to you to figure if that really explains everything. – Brian Oct 25 '11 at 18:51
  • 1
    @Brian: Yes, that was my general suspicion. Sounds like it's a 1GB-per-object limit on x86, and 2GB on x64. – Jon Skeet Oct 25 '11 at 18:54
  • @Brian and @JonSkeet, great sharing indeed, and with code: `for (int i = 0; i < 1000; i++) { msg += ", (" + test.Capacity + "," + i + ")"; test.Add(1); }` things are more clear. Apology for my formatting here; But atleast got the idea of inside:) Thanks. – Umer Oct 26 '11 at 06:10
  • 3
    Since .NET Framework 4.5 it is possible to enable arrays that are greater than 2Gb in total size for x64 processes. Just add <[gcAllowVeryLargeObjects](http://msdn.microsoft.com/en-us/library/hh285054(v=vs.110).aspx) enabled="True" /> element to runtime configuration of app.config file. – Peter Jan 03 '14 at 11:09
7

Here is an incredibly naive (and untested) implementation of a BigList backed by a Long instead of an integer. I wrote it in about 5 minutes, it doesn't implement ienumerable or ilist, but it shows the Partitioning that was mentioned in other answers. yeah, it's in VB, deal with it :)

This will need some pretty serious work and tuning up before it's actually useable, but it illustrates the idea.

Public Class BigList(Of T)
    Private mInternalLists As List(Of List(Of T))
    Private mPartitionSize As Integer = 1000000

    Private mSize As Long = 0

    Public Sub New()
        mInternalLists = New List(Of List(Of T))
    End Sub

    Public Sub Add(Item As T)
        mSize += 1

        Dim PartitionIndex As Integer = CInt(mSize \ mPartitionSize)

        Dim Partition As List(Of T)
        If mInternalLists.Count < PartitionIndex Then
            Partition = New List(Of T)
            mInternalLists.Add(Partition)
        Else
            Partition = mInternalLists(PartitionIndex)
        End If
        Partition.Add(Item)
    End Sub

    Default Public ReadOnly Property Item(Index As Long) As T
        Get
            Dim PartitionIndex As Integer = CInt(mSize \ mPartitionSize)
            Dim Partition As List(Of T)
            If mInternalLists.Count < PartitionIndex Then
                Throw New IndexOutOfRangeException
            Else
                Partition = mInternalLists(PartitionIndex)
            End If

            Return Partition(CInt(mSize Mod mPartitionSize))
        End Get
    End Property
End Class
Bradley Uffner
  • 16,641
  • 3
  • 39
  • 76
1

List limit is ~536,870,912 bytes (1/2 MB on my machine (32 bit Win7, .NET 4.0))

Your adding integers (4 bytes each), so limit is byte limit / 4 (~134,217,727)

Waters
  • 343
  • 1
  • 11
0

I didn't test it but due to its kind of implementation the LinkedList<T> should give you the possibility to add more elements than to a List<T>. But be aware of it's drawbacks (e.g. calling Count).

Oliver
  • 43,366
  • 8
  • 94
  • 151