【问题标题】:Fast lookup in a multidimensional System.Collection.IList在多维 System.Collection.IList 中快速查找
【发布时间】:2014-05-04 01:02:42
【问题描述】:

我需要您对以下方面的建议。

我有一个多维 IList,其中包含具有索引、ID 和文本的项目。通常我知道 Id 的值,并基于此我需要获取文本。 Id 和 Text 值都是从数据库中读取的。

我们目前用来获取Text字段值的是:

foreach (Object myObj in List) 
{
    if (((MessageType)myObj).Id == id) 
    {
        return ((MessageType)myObj).Text;
    }
}

当 IList 中的计数变大(超过 32K)时,需要一些时间来处理。

问题:有没有办法在不遍历 IList 的情况下有效地获取 Text 值?

我尝试过但没有成功的事情

  • 使用 List.IndexOf(Id) - 不起作用,因为 IndexOf 仅适用于文本。
  • 将 List 转换为多维数组 - 我猜测 List.CopyTo(array,0) 失败,因为它是多维的: 字符串[] 数组=新字符串[List.Count,List.Count]; List.CopyTo(array,0);

我不能使用 AJAX/JQuery 解决方案,因为它是一个现有(实时)项目,重新编码需要太多时间。

谢谢

【问题讨论】:

  • 您是否在同一数据集上多次执行此查找。还是只有一次?
  • 实际上是针对不同的ID进行了多次,因为它基本上是提供站点的菜单和子菜单;取决于网站的部分和开放区域的具体情况。
  • 您的 foreach 伪代码对我来说没有多大意义。那里的“多维”方面在哪里?它看起来像一个平面列表。
  • 我同意 Kevin 的观点,目前还不清楚 List 是什么。列表不是多维的,你有List<List<something>>吗?对此做了一些 LINQ 魔术,并意识到没有足够的信息来很好地回答。
  • @Kevin 当我读到“32K”时,很明显线性(一维或二维)数据结构在这里并不好。

标签: c# arrays performance multidimensional-array lookup


【解决方案1】:

如果您想通过某个标识符在包含 32k 元素的集合中快速搜索,您应该使用 Dictionary<K,V> 作为您的集合。

var dict = new Dictionary<IDType, MessageType>();

字典基本上是一个搜索树,其中元素以排序方式存储,因此无需查看所有元素即可找到具有特定键(在您的情况下为 ID)的元素。有关详细信息,请参阅MSDN

如果您不能将集合重构为字典,您可以先填充字典(慢),然后在字典中搜索(快)。如果您在再次填写字典之前进行多次搜索,即如果您的列表经常更改,这只会更快。

foreach(object o in List)
{
    var msg = (MessageType)o;
    dict.Add(msg.Id, msg);
}

然后搜索很容易:

MessageType msg = dict[id];

编辑:嗯,我很好奇,写了一个测试例程来比较线性搜索和字典方法。这是我使用的:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class MessageType
    {
        public string Id;
        public string Text;
    }

    class Program
    {
        static void Main(string[] args)
        {
            var rand = new Random ();
            // filling a list with random text messages
            List<MessageType> list = new List<MessageType>();
            for (int i = 0; i < 32000; i++)
            { 
                string txt = rand.NextDouble().ToString();
                var msg = new MessageType() {Id = i.ToString(), Text = txt };
                list.Add(msg);
            }
            IList List = (IList)list;

            // doing some random searches
            foreach (int some in new int[] { 2, 10, 100, 1000 })
            {
                var watch1 = new Stopwatch();
                var watch2 = new Stopwatch();
                Dictionary<string, MessageType> dict = null;
                for (int i = 0; i < some; i++)
                {
                    string id = rand.Next(32000).ToString();
                    watch1.Start();
                    LinearLookup(List, id);
                    watch1.Stop();

                    watch2.Start();
                    // fill once
                    if (dict == null)
                    {
                        dict = new Dictionary<string, MessageType>();
                        foreach (object o in List)
                        {
                            var msg = (MessageType)o;
                            dict.Add(msg.Id, msg);
                        }
                    }
                    // lookup 
                    DictionaryLookup(dict, id);
                    watch2.Stop();
                }

                Console.WriteLine(some + " x LinearLookup took " 
                    + watch1.Elapsed.TotalSeconds + "s");
                Console.WriteLine("Dictionary fill and " + some 
                    + " x DictionaryLookup took " 
                    + watch2.Elapsed.TotalSeconds + "s");
            }
        }

        static string LinearLookup(IList List, string id)
        {
            foreach (object myObj in List)
            {
                if (((MessageType)myObj).Id == id)
                {
                    return ((MessageType)myObj).Text;
                }
            }
            throw new Exception();
        }

        static string DictionaryLookup(Dictionary<string, MessageType> dict,
            string id)
        {
            return dict[id].Text;
        }
    }
}

我在 Release / x86 中得到的结果:

Number of | Time [ms] with | Time[ms] with | Speedup (approx.)
searches  | linear search  | dictionary(*) | with dictionary
----------+----------------+---------------+-----------------
      2   |      1.161     |   2.006       |   0.6
----------+----------------+---------------+-----------------
     10   |      2.834     |   2.060       |   1.4
----------+----------------+---------------+-----------------
    100   |     25.39      |   1.973       |   13
----------+----------------+---------------+-----------------
   1000   |    261.4       |   5.836       |   45
----------+----------------+---------------+-----------------

(*) including filling the dictionary once.

所以,我有点乐观地说,搜索两次就会有回报。在我的测试应用程序中,我必须搜索 10 次字典才能更快。

很抱歉,我无法举一个更现实的例子,我的 ID 都已排序。不过,请随意尝试修改和试验 ;-)

【讨论】:

  • 抱歉@helb,您的解决方案会使应用程序比现在更慢。
  • @AllBlond 这根本不是真的。 Dictionary 比任何 IList 都快。
  • 如果您填写字典一次并至少进行 2 次搜索,它已经比搜索列表两次要快得多。 (总是假设很多元素)
  • @helb 请尝试并报告。如果您要进行多次查找,它应该会更快。
  • 我会尝试,但从外观上看,我会遇到问题,因为 1.dictionary 将在每次调用我搜索的公共字符串时重新创建。 2. 我需要使用 IDictionary 而不是字典
【解决方案2】:

从外观上看,您在这里有一个List&lt;MessageType&gt;,它不是多维的。而是列表内部的对象有多个属性

你可以很容易地用 LINQ 比循环快得多:

var text = (from MessageType msgType in myList
            where msgType.Id == id
            select msgType.Text).FirstOrDefault();

使用内联 LINQ 语句甚至更容易:

var text = myList.Where(s => s.Id == id).Select(s => s.Text).FirstOrDefault();

注意:正如上面 cmets 中提到的,这些 LINQ 语句的速度仅与对象在列表中的位置一样好。如果它是列表中的最后一个对象,您可能会看到相同的性能差异。 Dictionary&lt;Index, MessageType&gt; 的性能会更高。

【讨论】:

  • 不,这不起作用,因为列表是 system.collection.IList 的类型,而不是 LINQ。来自 VS 的错误是:找不到 CAST。
  • 如何更快?我只是好奇,因为我认为这与发布的 OP 相同。
  • @AllBlond 你能发布实际的实现吗?你的伪代码不清楚。
  • @helb 我在其中添加了一条注释,说明 when 会更快。你的解决方案更好,我的只是一个可能会提高性能的替代方案。
  • 这是公共字符串的实际实现。 ID 被传递给它,List 被声明为全局变量,并在 onInit 期间在应用程序的其他地方填充。
【解决方案3】:

更好的方法是使用 ILookup。 例如:

var look = query.ToLookup(x => x.SomeID, y=> y.Name)

并使用:

if (look.Contains(myID)){
   var name = look[myID].First();
}

【讨论】:

    猜你喜欢
    • 2014-10-27
    • 2014-01-16
    • 1970-01-01
    • 2019-10-04
    • 2012-01-13
    • 2014-03-18
    • 1970-01-01
    • 1970-01-01
    • 2018-02-08
    相关资源
    最近更新 更多