Example to use a hashcode to detect if an element of a List<string> has changed C#

无人久伴 提交于 2019-12-12 23:31:37

问题


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");


    }

回答1:


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.




回答2:


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




回答3:


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.




回答4:


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.




回答5:


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;
    }
}


来源:https://stackoverflow.com/questions/5759288/example-to-use-a-hashcode-to-detect-if-an-element-of-a-liststring-has-changed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!