开发者

What can cause keys added to a Hashtable to be null?

Using the .NET Micro Framework 4.1

I'm adding the following string keys (and string values, not relevant here) to a hashtable:

"eth::address"
"eth::netmask"
"eth::gateway"
"eth::dns"
"eth::port"
"com::baudrate"
"com::parity"
"com::databits"
"com::stopbits"
"com::handshake"
"com::read-timeout"
"com::write-timeout"
"drv::led-firmware-file"
"scr::width"
"scr::height"
"scr::colors"

When adding these to the HashTable no errors are thrown.

However, when looking at the properties & content of the hashtable I can see the following:

16 buckets, but 6 开发者_开发问答of them have a null key and null value. It's always the same ones.

What could be causing this?

Update:

There's not much code to post:

var settings = new HashTable(16);
settings.Add("eth::address", "192.168.1.1");
//Keep adding the settings mentioned above

No exceptions are thrown, in the end there are 16 items in the hashtable, starting with 3 valid ones, then a few null ones, then a few valid ones, etc....

There's nothing else involved as this is a simply a test case

What can cause keys added to a Hashtable to be null?

If I try to get one of the values that "got lost", an exception is thrown:

var x = settings["eth::port"];

Will result in:

A first chance exception of type 'System.Exception' occurred in mscorlib.dll
An unhandled exception of type 'System.Exception' occurred in mscorlib.dll


enter code here


I don't have access to micro framework, but for .NET 4.0, i tested with sample you gave and it allocates 23 buckets, 7 of them has null values. Each value is placed in the bucket with its hash code % 23. For example, eth::gateway has hash code of 1866092901 and its modulus 23 is 22 so its placed in 22th bucket. Why do you worry about hash tables internal bucket allocation strategy? Try code below in Linqpad and you can be sure:

void Main()
{
    string[] vals = {"eth::address", "eth::netmask", .. all other strings... };
    var ht = new Hashtable(16);
    foreach (var v in vals) 
          ht[v] = v;
    var m = typeof(Hashtable).GetField("buckets", BindingFlags.NonPublic | BindingFlags.Instance);
    m.GetValue(ht).Dump();
    ht.Dump();
}


To extend Volkan's answer - checking the internal implementation of Hashtable one might find the following:

public Hashtable(int capacity) : this(capacity, (float) 1f)
{
}

public Hashtable(int capacity, float loadFactor)
{
    // arguments checking - elided

    this.loadFactor = 0.72f * loadFactor;
    double num = ((float) capacity) / this.loadFactor;
    if (num > 2147483647.0)
    {
        throw new ArgumentException(
            Environment.GetResourceString("Arg_HTCapacityOverflow"));
    }
    int num2 = (num > 3.0) ? HashHelpers.GetPrime((int) num) : 3;
    this.buckets = new bucket[num2];
    this.loadsize = (int) (this.loadFactor * num2);
    this.isWriterInProgress = false;
}

So what happens when you initialize it with new Hashtable(16)...? First, num's value is computed to 16/0.72 = 22.(2). Then, HashHelpers.GetPrime(22) kicks in, which looks like this:

internal static int GetPrime(int min)
{
    // arguments checking - elided

    for (int i = 0; i < primes.Length; i++)
    {
        int num2 = primes[i];
        if (num2 >= min)
        {
            return num2;
        }
    }

    // more code; irrelevant in this case - elided
}

Almost there. We only need to look up what primes is.

static HashHelpers()
{
    primes = new int[] { 3, 7, 11, 17, 23 /* more values */ };
}

With 22 as min argument, we can easily see GetPrime returns 23. And this is the value used in Hashtable constructor to create buckets array. You can perform same analysis for micro framework to see why it creates 16 buckets (which is weird TBH, considering it's good practice for buckets number to be prime value).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜