示例:使用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");
}
为什么不直接使用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;
}
}