示例:使用hashcode来检测List已经改变了c#

本文关键字:改变 string 使用 hashcode List 检测 示例 | 更新日期: 2023-09-27 17:51:26

我有一个基于一些XML元素的Linq查询每分钟更新一次的列表。

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

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

我尝试了一些简单的东西,如int test = list1.GetHashCode;但是无论列表中是什么,代码都是一样的…

这里是整个方法的链接查询和所有…注意最后的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");

    }

示例:使用hashcode来检测List<string>已经改变了c#

为什么不直接使用ObservableCollection来监视列表的变化呢?

如果你真的想散列整个列表,你可以这样做:

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

我认为MD5可能是多余的,你不需要一个加密安全的哈希函数来完成这个任务。

参考:字符串。连接和字符串。GetHashCode方法

如果你不打算有数十万个元素,或者如果你不打算每秒请求这个函数数千次,我认为你不需要为所有的哈希码讨论而烦恼。

下面是一个小程序,它会告诉你用正确的方法比较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();
    }
}

在我的电脑上花了0.001秒。

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

重写System.Object.GetHashCode的最佳算法是什么?

我在最近的一个项目中使用了这个,它工作得很好。您不一定需要使用加密哈希来获得好的哈希码,您可以自己计算,但不应该天真地完成。

您需要这样做:

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

现在你可以说:

int previousCode = list.GetHashCodeByElements();

几分钟后:

int currentCode = list.GetHashCodeByElements();
if(previousCode != currentCode) {
    // list changed
}

请注意,这可能会产生假阴性(列表更改了,但散列代码不会检测到它)。任何通过哈希码检测列表更改的方法都受此约束。

最后,根据您正在做的事情(如果有多个线程访问列表),您可能希望在计算哈希码和更新列表时考虑lock访问列表。这取决于你在做什么,这是否合适

如果您使用HashSet而不是List,您将获得更好的性能。HashSet使用其元素的哈希码来比较它们。这可能就是别人告诉你的。

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

HashSet实现与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;
    }
}