我有一个字符串值列表,其中一些值包含前面的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]);
}
}
我遇到的问题是只删除了部分值但不是全部。
是否有更好的方法可以删除这些值?
答案 0 :(得分:10)
使用RemoveAll方法
lstTitles.RemoveAll(s => s[0] == 'x' || s[0] == 'X');
您可能希望使用StartsWith
而不是比较第一个字符。
lstTitles.RemoveAll(s => s.StartsWith("x",StringComparison.InvariantCultureIgnoreCase));
答案 1 :(得分:3)
我遇到的问题是只删除了一些值但不是全部。
因为你正在跳过物品。当您致电Remove()
时,下一个项目将位于索引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()
中,这对于某些边缘情况来说更好,并且可能已经针对某些边缘情况进行了优化,因此再次手动编码它几乎没用。
答案 2 :(得分:2)
那是因为你跳过了价值。
假设您的列表包含['xVal1','xVal2','val3','xVal4','val5']。首先,您的i
为0,然后查看列表[0],即'xVal1',以便将其删除。
现在你的列表包含['xVal2','val3','xVal4','val5'],你的i
是1.所以你看一下列表[1]这是'val3'。你忽略了xVal2!
您可以从列表的后面开始并转到前面,但如果您删除了相同的值,您仍然会有潜在的错误。
更简单的方法是使用LINQ:
var newList = lstTitles.Where(title=>!title.StartsWith('xxx'))
答案 3 :(得分:2)
您应该使用允许传递ToLower
的{{1}}重载,而不是StartsWith
。
然后使用StringComparison.OrdinalIgnoreCase
这是最可读,最有效和最短的方法:
List.RemoveAll
答案 4 :(得分:2)
我认为,您最好以这种方式创建一个新列表
list = list
.Where(i => ! i.StartsWith("xxx", StringComparison.InvariantCultureIgnoreCase))
.ToList();
它将具有O(n)复杂度,而试图将1逐个删除将是O(n ^ 2)。
答案 5 :(得分:1)
这也可以起作用:
list.RemoveAll(i => i.StartsWith("xxx", StringComparison.InvariantCultureIgnoreCase));
处理所有案件而没有第二份清单。