Powershell Hashtable with Initial Capacity

Question

I was wondering how you declare a hashtable in powershell with an initial capacity. I know how large I want it to be but I have to assign the values to it in a loop.

So something like:

$hashtable = @{} (100)
Solution

JeroenMostert makes a good point as to why you may not need to specify an initial capacity:

Be aware that pre-specifying the capacity usually doesn't win you much in terms of memory or runtime, though; it already implements generous dynamic sizing, and if your guess is off, the benefits basically evaporate.

If you do need to specify the initial capacity:

Tip of the hat to PetSerAl for his help.

Because PowerShell's hashtables are always case-insensitive with respect to key lookups, [hashtable]::new(100) does not work, unfortunately, because the default is to create a case-sensitive hashtable.

Therefore, use of a [System.Collections.Hashtable] constructor overload that allows specifying the key-equality comparison method is required, so that you can specify a culture-sensitive, case-insensitive equality comparer to match PowerShell's usual hashtable behavior in v6.1-[1]:

# PSv5  syntax
$hashtable = [hashtable]::new(100, [StringComparer]::CurrentCultureIgnoreCase)

# PSv4- syntax
$hashtable = New-Object hashtable 100, ([StringComparer]::CurrentCultureIgnoreCase)

PetSerAl offers the following alternative:

$hashtable = [System.Collections.Specialized.CollectionsUtil]::CreateCaseInsensitiveHashtable(100)

Note: In v6.2 PowerShell now uses an ordinal string comparer ([StringComparer]::OrdinalIgnoreCase).

Additionally, as PetSerAl points out, PowerShell v6.1- caches the key-equality comparer for the current session - so an in-session change of the current culture is ignored. If you want to emulate this - questionable - behavior, PetSerAl provides the following command:

$hashtable = [hashtable]::new(100,
 [hashtable].GetProperty(
   'EqualityComparer',
   [System.Reflection.BindingFlags]'NonPublic, Instance'
 ).GetValue(@{}))

Despite the use of reflection to access a non-public property, this approach should be safe, because the targeted property's access modifier is protected, which means it has a "contract" with derived public classes and won't go away.


Note that another way to optimize a hashtable is to specify its load factor, and there are constructor overloads for specifying that factor as well.

From the docs (emphasis added):

A hash table's capacity is used to calculate the optimal number of hash table buckets based on the load factor. Capacity is automatically increased as required.

The load factor is the maximum ratio of elements to buckets. A smaller load factor means faster lookup at the cost of increased memory consumption.

When the actual load factor reaches the specified load factor, the number of buckets is automatically increased to the smallest prime number that is larger than twice the current number of buckets.


[1] Note that in many contexts PowerShell uses the invariant culture for string operations, but hashtables seem to be an exception - see this GitHub issue and this answer.
The v6.1- source code reveals the use of CurrentCultureIgnoreCase in PowerShell's hashtable constructor, and the v6.2 source code now shows use of ordinal (case-insensitive) comparison.