使用哈希码检测List 的元素是否已更改C#的示例

时间:2022-01-12 16:47:49

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

我有一个List,它根据一些XML元素的Linq查询每分钟更新一次。

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.

xml会不时发生变化。有人建议我可以使用Hashcode来确定列表中的任何字符串是否已更改。

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?

我已经看到了一些Md5哈希码计算的例子只是一个字符串,但不是列表......有人能告诉我一个用列表做这个的方法吗?

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

我试过像int test = list1.GetHashCode这样简单的东西;但无论列表中的内容如何,​​代码都是相同的......

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

这里是链接查询的整个方法,最后是所有..note SequenceEqual:

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


    }

5 个解决方案

#1


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.

这是一个小程序,它将向您展示使用正确的方法比较10000元素需要多长时间。

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.

在我的电脑上花了0.001秒。

#2


5  

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

为什么不只是使用ObservableCollection并监视列表的更改?

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.

我认为MD5可能过度,你不需要加密安全散列函数来完成这项任务。

Reference: String.Join and String.GetHashCode

参考:String.Join和String.GetHashCode

#3


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.

这是Jon Skeet的GetHashCode()实现仅供参考。请注意,您必须弄清楚如何将其用于比较列表/列表项所需的内容。

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

覆盖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


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.

最后,根据您正在做的事情(如果有多个线程命中列表),您可能需要考虑在计算哈希代码和更新列表时锁定对列表的访问权限。这取决于你正在做什么,不管这是否合适。

#5


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.

如果您将使用HashSet而不是List,您将获得更好的性能。 HashSet使用其元素的哈希码来比较它们。那可能就是你被告知的事情。

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

下一个示例演示了每次使用HashSet更改XML时如何更新列表并检测其中的更改。

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

HashSet实现与List相同的所有接口。因此,您可以在使用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;
    }
}

#1


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.

这是一个小程序,它将向您展示使用正确的方法比较10000元素需要多长时间。

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.

在我的电脑上花了0.001秒。

#2


5  

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

为什么不只是使用ObservableCollection并监视列表的更改?

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.

我认为MD5可能过度,你不需要加密安全散列函数来完成这项任务。

Reference: String.Join and String.GetHashCode

参考:String.Join和String.GetHashCode

#3


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.

这是Jon Skeet的GetHashCode()实现仅供参考。请注意,您必须弄清楚如何将其用于比较列表/列表项所需的内容。

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

覆盖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


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.

最后,根据您正在做的事情(如果有多个线程命中列表),您可能需要考虑在计算哈希代码和更新列表时锁定对列表的访问权限。这取决于你正在做什么,不管这是否合适。

#5


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.

如果您将使用HashSet而不是List,您将获得更好的性能。 HashSet使用其元素的哈希码来比较它们。那可能就是你被告知的事情。

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

下一个示例演示了每次使用HashSet更改XML时如何更新列表并检测其中的更改。

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

HashSet实现与List相同的所有接口。因此,您可以在使用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;
    }
}