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

时间:2011-04-22 19:34:12

标签: c# .net md5 hashcode generic-list

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

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

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

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

这是带有链接查询的整个方法,最后是所有..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 个答案:

答案 0 :(得分:5)

为什么不使用ObservableCollection并监控对列表的更改?

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

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

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

参考:String.JoinString.GetHashCode

答案 1 :(得分:0)

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

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

我在最近的一个项目中使用了它,效果很好。您不一定需要使用加密哈希来获得良好的哈希代码,您可以自己计算,但不应该天真地进行。

答案 2 :(得分:0)

你需要做这样的事情:

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对列表的访问。这取决于你正在做什么,不管这是否合适。

答案 3 :(得分:0)

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

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

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

答案 4 :(得分:0)

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

这是一个小程序,它将向您显示使用正确的方法比较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秒。