1

I have a List that updates every minute based on a Linq query of some XML elements.

the xml changes, from time to time. It was suggested to me that I could use Hashcode to determine if any of the strings in the list have changed.

I have seen some examples of Md5 hashcode calculations for just a string, but not for a list...could someone show me a way of doing this with a list?

I tried something simple like int test = list1.GetHashCode; but the code is the same no matter what is in the list...

here is the entire method with the link query and all..note the SequenceEqual at the end:

        private void GetTrackInfo()
    {
        _currentTitles1.Clear();
        var savedxmltracks = new XDocument();

        listBox1.Items.Clear();
        WebClient webClient = new WebClient();

        XmlDocument xmltracks = new XmlDataDocument();
        try
        {
            xmltracks.Load(_NPUrl);
            xmltracks.Save("xmltracks.xml");
        }
        catch (WebException ex)
        {
            StatusLabel1.Text = ex.Message;
        }

        try
        {
             savedxmltracks = XDocument.Load("xmltracks.xml");
        }
        catch (Exception ex)
        {
            StatusLabel1.Text = ex.Message;
        }


        var dateQuery = from c in savedxmltracks.Descendants("content")
                           select c;

        _count = savedxmltracks.Element("content").Element("collection").Attribute("count").Value;

        var tracksQuery1 = from c in savedxmltracks.Descendants("data")
                           select new
                           {
                               title = c.Attribute("title").Value,
                               imageurl = c.Attribute("image").Value,
                               price = c.Attribute("price").Value,
                               description = c.Attribute("productdescription").Value,
                               qualifier = c.Attribute("pricequalifier").Value

                           };

        var xml = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
        new XElement("LastUsedSettings",
            new XElement("TimerInterval",
                new XElement("Interval", Convert.ToString(numericUpDown1.Value))),
            new XElement("NowPlayingURL",
                new XElement("URL", _NPUrl)),
            new XElement("Email", emailAddress),
            new XElement("LastUpdated", DateTime.Now.ToString())));
        XElement StoreItems = new XElement("StoreItems");


        int i = 0;
        foreach (var c in tracksQuery1)
        {

            if (c.title.Length <= 40 & c.qualifier.Length <= 12 & i < 10)
            {

                if (c.title != null) _title1 = c.title;
                if (c.imageurl != null) _imageUrl = c.imageurl;
                if (c.price != null) _price = c.price;
                if (c.description != null) _productDescription = c.description;
                if (c.qualifier != null) _priceQualifier = c.qualifier;
                //}
                StoreItems.Add(new XElement("Title" + i.ToString(), _title1));
                _currentTitles1.Add(_title1);
                if (_oldTitles1.Count > 0)
                {
                    Console.WriteLine("OldTitle: {0}, NewTitle: {1}", _oldTitles1[i], _currentTitles1[i]);
                }
                StoreItems.Add(new XElement("Price" + i.ToString(), _price));
                StoreItems.Add(new XElement("Description" + i.ToString(), _productDescription));
                StoreItems.Add(new XElement("PriceQualifier" + i.ToString(), _priceQualifier));

                listBox1.Items.Add("Title: " + _title1);
                listBox1.Items.Add("Image URL: " + _imageUrl);
                listBox1.Items.Add("Price: " + _price);
                listBox1.Items.Add("Description: " + _productDescription);
                listBox1.Items.Add("PriceQualifier: " + _priceQualifier);

                try
                {
                    imageData = webClient.DownloadData(_imageUrl);
                }
                catch (WebException ex)
                {
                    StatusLabel1.Text = ex.Message;
                }

                MemoryStream stream = new MemoryStream(imageData);
                Image img = Image.FromStream(stream);
                //Image saveimage = img;
                //saveimage.Save("pic.jpg");

                img.Save("pic" + i.ToString() + ".jpg");

                stream.Close();



                i++;
            }
        }



        //Console.WriteLine("Count: " + _count);
        Console.WriteLine("oldTitles Count: " + _oldTitles1.Count.ToString());
        Console.WriteLine("currentTitles Count: " + _currentTitles1.Count.ToString());

        if (_oldTitles1.Count == 0) _oldTitles1 = _currentTitles1;

        if (!_oldTitles1.SequenceEqual(_currentTitles1))
        {
            Console.WriteLine("Items Changed!");
            SendMail();
            _oldTitles1 = _currentTitles1;
        }


        xml.Root.Add(StoreItems);
        xml.Save("settings.xml");


    }
casperOne
  • 73,706
  • 19
  • 184
  • 253
VulcanCCIT
  • 27
  • 1
  • 7
  • Do you want to figure out if *any* element of the list has changed, or *which specific* element(s) did change? – Lasse V. Karlsen Apr 22 '11 at 19:40
  • Do you care if the elements change order? – Hogan Apr 22 '11 at 19:45
  • Recommended reading: The accepted answer to [this question](http://stackoverflow.com/questions/462451/gethashcode-guidelines-in-c) contains a link to a blog post by Eric Lippert that explains why `list1.GetHashCode()` stays the same all the time. – takrl Apr 22 '11 at 19:54
  • 1
    GetHashCode() is not intended for this; ***please do not*** try doing it this way; it is a wrong direction – Marc Gravell Apr 22 '11 at 19:58
  • I dont need to know what is changed... just that anything changed..and Md5 is not necessary, but I just know that is related to hash discussions... I did edit my original post with what I am doing now, which seems to work... – VulcanCCIT Apr 22 '11 at 20:12

5 Answers5

4

why not just use an ObservableCollection and monitor changes to the list?

If you really wanted to hash an entire list, you might do something like this:

List<String> words;
int hash = String.Join("", words.ToArray()).GetHashCode();

I think MD5 may be overkill, you don't need a cryptographically secure hashing function for this task.

Reference: String.Join and String.GetHashCode

tbischel
  • 6,337
  • 11
  • 51
  • 73
  • I'd look at using the C5 collections library: http://www.itu.dk/research/c5/ You get all the observability and then some. Also, FWIW, MD5 isn't considered what you might call 'cryptographically secure' any more. – Nicholas Carey Apr 22 '11 at 21:14
  • According to microsoft, that method may change from 'one version of the common language runtime to another' - so probably not good to use if your storing the values in a db for longtime use – The Lemon Jun 07 '21 at 02:45
0

Here is Jon Skeet's GetHashCode() implementation just for reference. Note that you'll have to figure out how to work this into what you need for comparing the list/list items.

What is the best algorithm for an overridden System.Object.GetHashCode?

I used this in a recent project and it worked great. You don't necessarily need to use a cryptographic hash to get a good hash code, you can calculate it yourself, but it should not be done naively.

Community
  • 1
  • 1
Andy White
  • 86,444
  • 48
  • 176
  • 211
0

You need to do something like this:

public static class ListExtensions {
    private readonly static int seed = 17;
    private readonly static int multiplier = 23;
    public static int GetHashCodeByElements<T>(this List<T> list) {
        int hashCode = seed;
        for(int index = 0; index < list.Count; list++) {
            hashCode = hashCode * multiplier + list[index].GetHashCode();
        }
        return hashCode;
    }
}

Now you can say:

int previousCode = list.GetHashCodeByElements();

A few minutes later:

int currentCode = list.GetHashCodeByElements();

if(previousCode != currentCode) {
    // list changed
}

Note that this is subject to false negatives (the list changed, but the hash code won't detect it). Any method of detecting changes in a list via hash codes is subject to this.

Finally, depending on what you are doing (if there are multiple threads hitting the list), you might want to consider locking access to the list while computing the hash code and updating the list. It depends on what you're doing whether or not this is appropriate.

jason
  • 236,483
  • 35
  • 423
  • 525
0

You will have a better performance if you will use HashSet instead of List. HashSet uses hash codes of its element to compare them. That’s probably what you were told about.

The next example demonstrates how to update you list and detect changes in it every time your XML is changed using HashSet.

HashSet implement all the same interfaces as List. Thus, you can easily use it everywhere where you used your List.

 public class UpdatableList
{
    public HashSet<string> TheList { get; private set; }

    //Returns true if new list contains different elements
    //and updates the collection.
    //Otherwise returns false.
    public bool Update(List<String> newList)
    {
        if (TheList == null)
        {
            TheList = new HashSet<string>(newList);
            return true;
        }

        foreach (var item in newList)
        {
            //This operation compares elements hash codes but not 
            //values itself.
            if (!TheList.Contains(item))
            {
                TheList = new HashSet<string>(newList);
                return true;
            }
        }

        //It gets here only if both collections contain identical strings.
        return false;
    }
}
Dennis
  • 2,615
  • 2
  • 19
  • 20
  • probably so...is this similar to what SequenceEqual does behind the scenes? – VulcanCCIT Apr 22 '11 at 20:01
  • Indeed, SequenceEqual uses GetHashCode underneath. The only difference is that it hashes the original collection every time you perform comparison. – Dennis Apr 22 '11 at 20:08
  • I mean the difference with HashSet. HashSet pre-hash all elements only once. – Dennis Apr 22 '11 at 20:09
  • someone asked for me to post the code, I can post the entire method..would I do that by answering my own question or just editing the origional? This forum is amazingly FAST with answers!!! first time here! – VulcanCCIT Apr 22 '11 at 20:16
  • 1
    `HashSet` is an implementation of a *set*. Each element in a set is unique. Sets also have no ordering, implicit or otherwise. Lists, on the other hand, are quite specifically ordered and allow duplication. Without knowing the OPs actual requirements, `HashSet` is not a substitute for `List`. – Nicholas Carey Apr 22 '11 at 21:03
  • I agree. HashSet is not an optimal choice if you can guaranty that both collections are ordered in the same way. – Dennis Apr 23 '11 at 15:35
0

I don’t think you need to bother yourself about all the hash code discussion if you are not going to have hundreds thousands of elements or if you are not going to request this function thousands times a second.

Here is a small program that will show you how much time it will take to compare 10000 element using your correct way of doing this.

class Program
{
    static void Main(string[] args)
    {
        var list1 = new List<string>();
        var list2 = new List<string>();
        for (int i = 0; i < 10000; i++)
        {
            list1.Add("Some very very very very very very very long email" + i);
            list2.Add("Some very very very very very very very long email" + i);
        }

        var timer = new Stopwatch();
        timer.Start();
        list1.SequenceEqual(list2);
        timer.Stop();
        Console.WriteLine(timer.Elapsed);
        Console.ReadKey();
    }
}

At my PC it took 0.001 seconds.

Dennis
  • 2,615
  • 2
  • 19
  • 20
  • Dennis, it looks like my SequenceEqual will be just fine based on that...I only was investigating based on an experienced programmers suggestion, but sometimes I think experienced programmers get overly complicated lol. This list is amazing! can I mark all of these answers as helpful? They all were to me! – VulcanCCIT Apr 22 '11 at 20:31