2

我有一个字符串值列表,其中一些值在前面包含 xxx 或 XXX。

xxxRed
xxxYellow
xxxxCareful with that axe Eugene!
xxxxxxdedicum aceasta frumoasa Melodia
xxxxLeaders
xxxxWorking Around - titles
XXXXXNothing To Fear
xxxxAvoiding standards
xxxFirst Aid

List<string> lstTitles = new List<string>();

这是我尝试过的

for (int i=0; i < lstTitles.Count; i++)
            {
                string title = lstTitles[i].ToLower().Trim();
                if (title[0] == 'x')
                {
                    lstTitles.Remove(lstTitles[i]);

                }
            }

我遇到的问题是只删除了一些值,但不是全部。

是否有更好的方法来删除这些值?

4

6 回答 6

10

使用RemoveAll方法

lstTitles.RemoveAll(s => s[0] == 'x' || s[0] == 'X');

您可能想使用StartsWith而不是比较第一个字符。

lstTitles.RemoveAll(s => s.StartsWith("x",StringComparison.InvariantCultureIgnoreCase));
于 2013-06-06T11:15:23.567 回答
3

我遇到的问题是只有一些值被删除,但不是全部。

因为您正在跳过项目。当您调用Remove()时,下一个项目将位于 index i,但您将i在下一个循环中增加。

它可以通过迭代列表的副本并删除原始列表中不需要的项目来解决:

foreach (var item in lstTitles.ToList())
{
    if (item.StartsWith("x", StringComparison.InvariantCultureIgnoreCase))
    {
        lstTitles.Remove(item);
    }
}

虽然这涉及创建列表的副本,这并不是真正有用,以及调用Remove()本身远非高性能。

所以你可以反转你的for循环,首先删除最后一个项目,这不会改变未处理项目的索引:

for (int i = lstTitles.Count - 1; i > 0; i--)
{
    if (lstTitles[i].StartsWith("x", StringComparison.InvariantCultureIgnoreCase))
    {
        lstTitles.RemoveAt(i);
    }
}

但是正如@I4V 指出的那样,所有这些逻辑都已经在 中List<T>.RemoveAll(),这更易于阅读并且可能针对某些边缘情况进行了优化,因此再次手动编码几乎没有用处。

于 2013-06-06T11:14:41.377 回答
2

那是因为您的跳过值。

假设您的列表包含 ['xVal1', 'xVal2', 'val3', 'xVal4', 'val5']。起初你i是 0,然后你查看 list[0],它是 'xVal1',所以你删除它。

Now your list contains ['xVal2', 'val3', 'xVal4', 'val5'], and your i is 1. So you look at list[1] which is 'val3'. You ignored xVal2 !

You can start at the back of the list and go to the front, although you will still have a potential bug in case there are identical values you remove.

A shorter way would be to use LINQ:

var newList = lstTitles.Where(title=>!title.StartsWith('xxx'))
于 2013-06-06T11:16:23.293 回答
2

Instead of ToLower you should use the overload of StartsWith which allows to pass a StringComparison.OrdinalIgnoreCase.

Then use List.RemoveAll which is the most readable, most efficient and shortest approach:

lstTitles.RemoveAll(s => s.TrimStart().StartsWith("x", StringComparison.OrdinalIgnoreCase));

Demo

于 2013-06-06T11:16:39.723 回答
2

I think, you'd better just create a new list this way

list = list
    .Where(i => ! i.StartsWith("xxx", StringComparison.InvariantCultureIgnoreCase))
    .ToList();

It would have a O(n) complexity whereas, trying to remove then 1 by 1 would be in O(n^2).

于 2013-06-06T11:17:41.883 回答
1

This could work also :

list.RemoveAll(i => i.StartsWith("xxx", StringComparison.InvariantCultureIgnoreCase));

Handles all cases and without a second list.

于 2013-06-06T11:24:34.220 回答