You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WinForms中如何高效搜索ListBox项是否存在于长字符串中?

优化ListBox大量项匹配长字符串的C#实现

你当前的代码在处理20万+项时会遇到严重的性能问题,核心瓶颈有两个:

  1. 字符串拼接用+操作:每次拼接都会生成新的字符串实例,20万次操作会产生大量临时对象,GC压力陡增,执行速度极慢。
  2. 逐个调用Result.Contains(itm):每次都是对长字符串做线性扫描,时间复杂度为O(N*M)(N是项数,M是长字符串长度),20万次扫描的耗时会非常夸张。

下面针对不同场景给出优化方案:

场景1:待匹配项是长字符串中的独立元素

如果Result里的内容是用逗号、空格这类分隔符拆分的独立元素,先把Result转成哈希集合,将查找操作的时间复杂度降到O(1),同时用StringBuilder处理拼接:

// 根据实际分隔符拆分Result,这里假设是逗号
var resultElements = new HashSet<string>(Result.Split(',', StringSplitOptions.RemoveEmptyEntries));
var sb = new StringBuilder();

foreach (string item in lbxCodes.Items.Cast<string>())
{
    if (resultElements.Contains(item))
    {
        if (sb.Length > 0)
            sb.Append(',');
        sb.Append(item);
    }
}

return sb.ToString();

场景2:待匹配项是长字符串中的任意子串

如果项可能是Result里的任意子串(比如"abc"出现在"xyzabc123"中),用Aho-Corasick多模式匹配算法可以一次性完成所有模式的匹配,时间复杂度降到O(M + N + K)(M是长字符串长度,N是所有项的总长度,K是匹配结果数),效率远高于逐个调用Contains。

这里提供一个简化版的Aho-Corasick实现,结合StringBuilder使用:

// 简化版Aho-Corasick多模式匹配类
public class AhoCorasickMatcher
{
    private class Node
    {
        public Dictionary<char, Node> Children { get; } = new();
        public Node Fail { get; set; }
        public List<string> Matches { get; } = new();
    }

    private readonly Node _root;

    public AhoCorasickMatcher(IEnumerable<string> patterns)
    {
        _root = new Node();
        // 构建字典树
        foreach (var pattern in patterns)
        {
            var current = _root;
            foreach (var c in pattern)
            {
                if (!current.Children.TryGetValue(c, out var child))
                {
                    child = new Node();
                    current.Children[c] = child;
                }
                current = child;
            }
            current.Matches.Add(pattern);
        }
        // 构建失败指针
        var queue = new Queue<Node>();
        foreach (var child in _root.Children.Values)
        {
            child.Fail = _root;
            queue.Enqueue(child);
        }
        while (queue.Count > 0)
        {
            var current = queue.Dequeue();
            foreach (var (c, child) in current.Children)
            {
                var failNode = current.Fail;
                while (failNode != null && !failNode.Children.ContainsKey(c))
                    failNode = failNode.Fail;
                child.Fail = failNode?.Children.GetValueOrDefault(c) ?? _root;
                child.Matches.AddRange(child.Fail.Matches);
                queue.Enqueue(child);
            }
        }
    }

    public HashSet<string> FindAllMatches(string text)
    {
        var matches = new HashSet<string>();
        var current = _root;
        foreach (var c in text)
        {
            while (current != null && !current.Children.ContainsKey(c))
                current = current.Fail;
            current = current?.Children.GetValueOrDefault(c) ?? _root;
            foreach (var match in current.Matches)
                matches.Add(match);
        }
        return matches;
    }
}

// 使用示例
var allItems = lbxCodes.Items.Cast<string>();
var matcher = new AhoCorasickMatcher(allItems);
var matchedItems = matcher.FindAllMatches(Result);

var sb = new StringBuilder();
foreach (var item in matchedItems)
{
    if (sb.Length > 0)
        sb.Append(',');
    sb.Append(item);
}

return sb.ToString();

额外提示

  • 不管哪种场景,都要用lbxCodes.Items.Cast<string>()避免装箱拆箱的性能开销,20万项的情况下这个优化很有必要。
  • 如果用.NET Core/.NET 5+,也可以尝试把所有项转成正则表达式(用|分隔),但要注意转义特殊字符,且20万项的正则表达式可能会过长,性能不如Aho-Corasick稳定。

内容的提问来源于stack exchange,提问作者asmgx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 15:13:14