在多维System.Collection.IList中快速查找

时间:2014-03-25 22:01:33

标签: c# arrays performance multidimensional-array lookup

我需要你的建议如下。

我有一个多维IList包含具有索引,Id和Text的项目。通常我知道Id的值,并根据我需要获取文本。 Id和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)上失败了我的猜测,因为它是多维的: string [] array = new string [List.Count,List.Count]; List.CopyTo(阵列,0);

我无法使用AJAX / JQuery解决方案,因为它是一个现有的(实时)项目,重新编码将花费太多。

由于

3 个答案:

答案 0 :(得分:4)

如果您希望通过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次以使字典更快。

对不起,我无法做出更实际的例子,我的Ids全部排序。尽管可以尝试修改和试验; - )

答案 1 :(得分:1)

从它的外观来看,这里有List<MessageType>,这不是多维的。而是列表中的 对象具有多个属性

使用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();

注意:如上面的注释中所述,这些LINQ语句的速度仅与对象在List中的位置一样好。如果它是列表中的最后一个对象,您可能会看到相同的性能差异。 Dictionary<Index, MessageType>将会更高效。

答案 2 :(得分:0)

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

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

并使用:

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *


class stackedExample(QWidget):

    def __init__(self):
        super(stackedExample, self).__init__()
        self.rightlist = QListWidget()
        self.rightlist.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)

        self.rightlist.insertItem(0, 'Contact')

        self.stack1 = QWidget()
        self.stack1.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Minimum)

        self.stack1UI()

        self.Stack = QStackedWidget(self)
        self.Stack.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Minimum)
        self.Stack.addWidget(self.stack1)

        hbox = QHBoxLayout(self)
        hbox.addWidget(self.Stack)
        hbox.addWidget(self.rightlist)


        self.setLayout(hbox)
        self.rightlist.currentRowChanged.connect(self.display)
        self.setGeometry(300, 50, 10, 10)
        self.setWindowTitle('StackedWidget demo')
        self.show()


    def stack1UI(self):
        layout = QVBoxLayout()
        label = QLabel("Hello World")
        label.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Minimum)
        label.setStyleSheet("QLabel { background-color : red; color : blue; }")
        layout.addWidget(label)
        self.stack1.setLayout(layout)


    def display(self, i):
        self.Stack.setCurrentIndex(i)

def main():
    app = QApplication(sys.argv)
    ex = stackedExample()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()