find 和 findindex 对于 List 来说非常缓慢;为什么?
发布于 2025-01-06 19:46:27 字数 3901 浏览 1 评论 0 原文

我正在一个密集使用 List 的项目中工作,我尝试通过名称(即对象的成员)查找对象。

我的代码无需使用单个 for-next 循环(函数 find1)进行搜索即可工作,但我发现可以使用内置的 find find 进行相同的操作,并且代码可以工作。不过,感觉有点慢。所以,我做了一个项目来测试速度:

我有下一个代码

    public List<MyObject> varbig = new List<MyObject>();
    public Dictionary<string,string> myDictionary=new Dictionary<string, string>();
    public Form1() {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e) {
        myDictionary.Clear();
        varbig.Clear();

        for (int i = 0; i < 5000; i++) {
            varbig.Add(new MyObject("name" + i.ToString(),"value"+i.ToString()));
            myDictionary.Add("name" + i.ToString(), i.ToString());
        }
        // first test
        var start1 = Environment.TickCount;
        for (int i = 0; i < 3000; i++) {
            var ss=find1("name499");
        }
        var end1 = Environment.TickCount;
        Console.WriteLine("time  1 :" + (end1 - start1));
        // second test
        var start2 = Environment.TickCount;
        for (int i = 0; i < 3000; i++) {
            var ss=find2("name499");
        }
        var end2 = Environment.TickCount;
        Console.WriteLine("time  2 :" + (end2 - start2));
        // third test
        var start3 = Environment.TickCount;
        for (int i = 0; i < 3000; i++) {
            var ss = find3("name499");
        }
        var end3 = Environment.TickCount;
        Console.WriteLine("time  3 :" + (end3 - start3));

        // first test b
        var start1b = Environment.TickCount;
        for (int i = 0; i < 3000; i++) {
            var ss=find1("name4999");
        }
        var end1b = Environment.TickCount;
        Console.WriteLine("timeb 1 :" + (end1b - start1b));
        // second test
        var start2b = Environment.TickCount;
        for (int i = 0; i < 3000; i++) {
            var ss=find2("name4999");
        }
        var end2b = Environment.TickCount;
        Console.WriteLine("timeb 2 :" + (end2b - start2b));
        // third test
        var start3b = Environment.TickCount;
        for (int i = 0; i < 3000; i++) {
            var ss = find3("name4999");
        }
        var end3b = Environment.TickCount;
        Console.WriteLine("timeb 3 :" + (end3b - start3b));

    }

    public int find1(string name) {
        for (int i = 0; i < varbig.Count; i++) {
            if(varbig[i].Name == name) {
                return i;
            }
        }
        return -1;
    }

    public int find2(string name) {
        int idx = varbig.FindIndex(tmpvar => Name == name);
        return idx;
    }
    public int find3(string name) {
        var ss=myDictionary[name];
        return int.Parse(ss);
    }
}

并且我使用下一个对象

public class MyObject {
    private string _name = "";
    private string _value = "";
    public MyObject() {}

    public MyObject(string name, string value) {
        _name = name;
        _value = value;
    }

    public string Name {
        get { return _name; }
        set { _name = value; }
    }

    public string Value {
        get { return _value; }
        set { _value = value; }
    }
}

大多数情况下它会做接下来的事情: 我创建了一个包含 5000 个元素的数组。

time 1 = 使用简单的 for-next 搜索第 499 个对象(索引)。

时间 2 = 使用 List 的内置函数 find 搜索第 499 个元素

时间 3 = 使用字典搜索第 499 个元素。

Timeb 1、timeb 2 和 timeb 3 执行相同操作,但尝试搜索第 4999 个元素而不是第 499 个元素。

我跑了几次:

  • time 1 :141
  • time 2 :1248
  • time 3 :0
  • timeb 1 :811
  • timeb 2 :1170
  • timeb 3 :0

  • time 1 :109< /p>

  • 时间 2:1170
  • 时间 3:0
  • 时间b 1:796
  • timeb 2 :1170
  • timeb 3 :0

(越小则越快)

而且,令我惊讶的是,内置函数 findindex 慢得离谱(在某些情况下,接近慢 10 倍,而且,字典方法几乎是即时的。

我的问题是,为什么?是因为谓词吗?

Im am working in a project that uses intensively List and i try to find the object via the name (that is a member of the object).

My code worked without searching it using a single for-next loop (function find1) but i found that it is possible to the same using the build-in found find, and the code works. However, it feel a bit slow. So, i did a project for test the speed:

I have the next code

    public List<MyObject> varbig = new List<MyObject>();
    public Dictionary<string,string> myDictionary=new Dictionary<string, string>();
    public Form1() {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e) {
        myDictionary.Clear();
        varbig.Clear();

        for (int i = 0; i < 5000; i++) {
            varbig.Add(new MyObject("name" + i.ToString(),"value"+i.ToString()));
            myDictionary.Add("name" + i.ToString(), i.ToString());
        }
        // first test
        var start1 = Environment.TickCount;
        for (int i = 0; i < 3000; i++) {
            var ss=find1("name499");
        }
        var end1 = Environment.TickCount;
        Console.WriteLine("time  1 :" + (end1 - start1));
        // second test
        var start2 = Environment.TickCount;
        for (int i = 0; i < 3000; i++) {
            var ss=find2("name499");
        }
        var end2 = Environment.TickCount;
        Console.WriteLine("time  2 :" + (end2 - start2));
        // third test
        var start3 = Environment.TickCount;
        for (int i = 0; i < 3000; i++) {
            var ss = find3("name499");
        }
        var end3 = Environment.TickCount;
        Console.WriteLine("time  3 :" + (end3 - start3));

        // first test b
        var start1b = Environment.TickCount;
        for (int i = 0; i < 3000; i++) {
            var ss=find1("name4999");
        }
        var end1b = Environment.TickCount;
        Console.WriteLine("timeb 1 :" + (end1b - start1b));
        // second test
        var start2b = Environment.TickCount;
        for (int i = 0; i < 3000; i++) {
            var ss=find2("name4999");
        }
        var end2b = Environment.TickCount;
        Console.WriteLine("timeb 2 :" + (end2b - start2b));
        // third test
        var start3b = Environment.TickCount;
        for (int i = 0; i < 3000; i++) {
            var ss = find3("name4999");
        }
        var end3b = Environment.TickCount;
        Console.WriteLine("timeb 3 :" + (end3b - start3b));

    }

    public int find1(string name) {
        for (int i = 0; i < varbig.Count; i++) {
            if(varbig[i].Name == name) {
                return i;
            }
        }
        return -1;
    }

    public int find2(string name) {
        int idx = varbig.FindIndex(tmpvar => Name == name);
        return idx;
    }
    public int find3(string name) {
        var ss=myDictionary[name];
        return int.Parse(ss);
    }
}

And i use the next object

public class MyObject {
    private string _name = "";
    private string _value = "";
    public MyObject() {}

    public MyObject(string name, string value) {
        _name = name;
        _value = value;
    }

    public string Name {
        get { return _name; }
        set { _name = value; }
    }

    public string Value {
        get { return _value; }
        set { _value = value; }
    }
}

Mostly it do the next thing:
I create an array with 5000 elements.

time 1 = search the 499th object (index) using a simple for-next.

time 2 = search the 499th using the build in function find of List

time 3 = it do the search of the 499th element using dictionary.

Timeb 1, timeb 2 and timeb 3 do the same but try to search the 4999th element instead of the 499th element.

I ran a couple of times :

  • time 1 :141
  • time 2 :1248
  • time 3 :0
  • timeb 1 :811
  • timeb 2 :1170
  • timeb 3 :0

  • time 1 :109

  • time 2 :1170
  • time 3 :0
  • timeb 1 :796
  • timeb 2 :1170
  • timeb 3 :0

(the small then the fast)

And, for my surprise, the build in function findindex is absurdly slow (in some cases, close to 10x slower. Also, the dictionary approach is almost instantly.

My question is, why?. is it because the predicate?.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

戏蝶舞 2025-01-13 19:46:28

问题出在这一行:

int idx = varbig.FindIndex(tmpvar => Name == name);

Name == name是错误的,你应该写tmpvar.Name == name

在您的代码中,您将 name 参数与表单的 Name 属性进行比较;它们显然是不同的,因此该方法总是检查整个列表,而不是在找到搜索值时停止。事实上,正如您从数字中看到的那样,find2() 花费的时间基本上总是相等的。

关于字典,它显然比其他方法更快,因为字典是专门为提供快速键控访问而构建的内存结构。

事实上,它们的时间复杂度接近 O(1),而循环列表时,时间复杂度等于 O(n)

The problem is in this line:

int idx = varbig.FindIndex(tmpvar => Name == name);

Name == name is wrong, you should write tmpvar.Name == name instead.

In your code you're comparing name argument with the Name property of your form; they are obviously different, and so the method always examines the whole list instead of stopping when the searched value is found. In fact, as you can see looking the numbers, the time spent by find2() is basically always equal.

About the dictionary, it's obviously faster than the other methods because dictionaries are memory structure specifically built to provide fast keyed access.

In fact they arrive close to O(1) time complexity, while looping a list you have a time complexity equal to O(n).

吻风 2025-01-13 19:46:28
  • Find1 使用一个简单的 for( i = 0 to count) 方法
  • Find2 使用内置的 Find 方法(与上面的 find1 完全相同),只是您已经传递了一个谓词,我认为这会减慢速度。
  • 使用字典的 Find3,我认为在没有任何计时器的情况下是最快的,因为字典在幕后使用哈希表,它具有 0(1) 查找(连续时间)
  • Find1 is using a simple for( i = 0 to count) method
  • Find2 uses the built in Find method (which is exactly find1 above), except that you have passed a predicate along with it, which I believe is slowing it down.
  • Find3 using a dictionary, I would assume is the fastest without any timers, becuase a dictionary uses hashtables under the covers which has an 0(1) look up (contant time)
话少心凉 2025-01-13 19:46:28

您的代码中存在错误 - find2 方法使用 Form.Name 进行比较,而不是您的集合对象名称。它应该看起来像这样:

public int find2(string name) {
    return varbig.FindIndex((obj) => obj.Name == name);
}

不使用 Form.Name 的结果更加一致:

time  1 :54
time  2 :50
time  3 :0
timeb  1 :438
timeb  2 :506
timeb  3 :0

There is the error in your code - the find2 method uses the Form.Name for the comparison instead of your collection objects names. It should looks like this:

public int find2(string name) {
    return varbig.FindIndex((obj) => obj.Name == name);
}

The results without using the Form.Name are more consistent:

time  1 :54
time  2 :50
time  3 :0
timeb  1 :438
timeb  2 :506
timeb  3 :0
め可乐爱微笑 2025-01-13 19:46:28

您不需要使用 for 循环在 find2 中进行搜索...

只需直接调用 find2,结果将为 0。

You don't need to put for loop to search in find2...

Just call find2 directly, then result will be 0.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文