5

我有一个字符串数组 x 和一个列表 y,我想从列表 X 中删除 Y 中的所有数据,如何以最快的方式做到这一点?

例如:X:1)“aaa.bbb.ccc”2)“ddd.eee.fff”3)“ggg.hhh.jjj”

Y:1)“bbb” 2)“fff”

结果应该是一个新列表,其中只有 3) 存在,因为 X.1 被 Y.1 删除,X.2 被 Y.2 删除

怎么做?

我知道我可以在 List X 上做一个 foreach 并检查 List Y 中的所有内容,这是最快的方法吗?

4

5 回答 5

9

方便的是

var Z = X.Where(x => !x.Split('.').Intersect(Y).Any()).ToList();

这与“最快”不同。可能最快(运行时)的方法是使用令牌搜索,例如:

public static bool ContainsToken(string value, string token, char delimiter = '.')
{
    if (string.IsNullOrEmpty(token)) return false;
    if (string.IsNullOrEmpty(value)) return false;

    int lastIndex = -1, idx, endIndex = value.Length - token.Length, tokenLength = token.Length;
    while ((idx = value.IndexOf(token, lastIndex + 1)) > lastIndex)
    {
        lastIndex = idx;
        if ((idx == 0 || (value[idx - 1] == delimiter))
            && (idx == endIndex || (value[idx + tokenLength] == delimiter)))
        {
            return true;
        }
    }
    return false;
}

然后是这样的:

var list = new List<string>(X.Length);
foreach(var x in X)
{
    bool found = false;
    foreach(var y in Y)
    {
        if(ContainsToken(x, y, '.'))
        {
            found = true;
            break;
        }
    }
    if (!found) list.Add(x);
}

这个:

  • 不分配数组(对于 的输出Split,对于params char[]of Split
  • 不创建任何新string实例(用于 的输出Split
  • 不使用委托抽象
  • 没有捕获的范围
  • 使用struct自定义迭代器List<T>而不是class迭代器IEnumerable<T>
  • 以适当的最坏情况大小开始新List<T>的,以避免重新分配
于 2013-10-23T12:26:50.020 回答
1

试试这个,使用Aggregate函数

    var xArr = new string[] { "aaa.bbb.ccc", "ddd.eee.fff", "ggg.hhh.jjj" };
    var yList = new List<string> { "bbb", "fff" };

    var result = xArr.Aggregate(new List<string> { }, (acc, next) =>
    {
        var elems = next.Split('.');
        foreach (var y in yList)
            if (elems.Contains(y))
                return acc;
        acc.Add(next);
        return acc;
    });
于 2013-10-23T12:40:36.430 回答
1

我认为一个相当快的方法是使用 List 的内置RemoveAll()方法:

List<string> x = new List<string>
{
    "aaa.bbb.ccc",
    "ddd.eee.fff",
    "ggg.hhh.jjj"
};

List<string> y = new List<string>
{
    "bbb",
    "fff"
};

x.RemoveAll(s => y.Any(s.Contains));

(请注意,我假设您有两个列表,x 和 y。您的 OP 提到了一个字符串数组,但随后继续谈论“列表 X”和“列表 Y”,所以我忽略了字符串数组位。)

于 2013-10-23T12:30:31.763 回答
1

迭代 X 和 Y 确实是最快的选择,因为你有这个包含约束。我真的没有看到任何其他方式。

不过,它应该是foreachover X,因为您无法修改使用foreach.

所以一个选择是:

for (int counterX = 0; counterX < X.Length; counterX++)
{
    for(int counterY = 0; counterY < Y.Length; counterY++)
    {
        if (X[counterX].Contains(Y[counterY]))
        {
            X.RemoveAt(counterX--);
            counterY = Y.Length;
        }
    }
}

应该这样做(请注意,此代码未经测试)。

于 2013-10-23T12:25:04.180 回答
0

如果您有一个相对较小的列表,那么性能影响并不是什么大问题。这是我能想到的最简单的 foreach 解决方案。

List<string> ListZ = ListX.ToList();

foreach (string x in ListX)
{
    foreach (string y in ListY)
    {
        if (x.Contains(y))
            ListZ.Remove(x);
    }
}
于 2013-10-23T12:33:12.217 回答