Пример использования хеш-кода для определения, изменился ли элемент списка <string>C # - PullRequest
1 голос
/ 22 апреля 2011

У меня есть список, который обновляется каждую минуту на основе запроса Linq некоторых элементов XML.

время от времени меняется 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");


    }

Ответы [ 5 ]

5 голосов
/ 22 апреля 2011

почему бы просто не использовать ObservableCollection и отслеживать изменения в списке?

Если вы действительно хотите хэшировать весь список, вы можете сделать что-то вроде этого:

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

Я думаю, что MD5 может быть излишним, вам не нужна криптографически безопасная функция хеширования дляэта задача.

Ссылка: String.Join и String.GetHashCode

0 голосов
/ 23 апреля 2011

Я не думаю, что вам нужно беспокоиться обо всем обсуждении хеш-кода, если у вас не будет сотен тысяч элементов или если вы не собираетесь запрашивать эту функцию тысячи раз в секунду.

Вот небольшая программа, которая покажет вам, сколько времени потребуется для сравнения 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 секунды.

0 голосов
/ 22 апреля 2011

У вас будет лучшая производительность, если вы будете использовать HashSet вместо List.HashSet использует хеш-коды своего элемента для их сравнения.Это, вероятно, то, о чем вам сказали.

Следующий пример демонстрирует, как обновлять ваш список и обнаруживать изменения в нем каждый раз, когда ваш XML изменяется с помощью HashSet.

HashSet реализует все те же интерфейсы, что иСписок.Таким образом, вы можете легко использовать его везде, где вы использовали свой список.

 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;
    }
}
0 голосов
/ 22 апреля 2011

Вам нужно сделать что-то вроде этого:

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 доступ к списку при вычислении хеш-кода и обновлении списка.Это зависит от того, что вы делаете, является ли это уместным.

0 голосов
/ 22 апреля 2011

Вот реализация GetHashCode() Джона Скита только для справки.Обратите внимание, что вам нужно выяснить, как это сделать для сравнения элементов списка / списка.

Каков наилучший алгоритм для переопределенного System.Object.GetHashCode?

Я использовал это в недавнем проекте, и он отлично работал.Вам не обязательно использовать криптографический хеш для получения хорошего хеш-кода, вы можете вычислить его самостоятельно, но это не следует делать наивно.

...