从列表中删除特定的字符串值
本文关键字:字符串 列表 删除 | 更新日期: 2023-09-27 18:17:07
我有一个字符串值列表,其中一些值在前面包含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]);
}
}
我的问题是,只有一些值被删除,但不是全部。
是否有更好的方法来删除这些值?
使用RemoveAll方法
lstTitles.RemoveAll(s => s[0] == 'x' || s[0] == 'X');
,你可能想用StartsWith
代替比较第一个字符。
lstTitles.RemoveAll(s => s.StartsWith("x",StringComparison.InvariantCultureIgnoreCase));
我的问题是,只有一些值被删除,但不是全部。
因为你跳过了项目。当您调用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()
中,这是更好的阅读和可能优化了一些边缘情况,所以没有什么用处再手工编码。
这是因为你的跳过值。
假设列表包含['xVal1', 'xVal2', 'val3', 'xVal4', 'val5']。一开始你的i
是0,你看list[0],它是'xVal1',所以你删除它。
现在您的列表包含['xVal2', 'val3', 'xVal4', 'val5'],并且您的i
为1。你看一下list[1],它是'val3'。您忽略了xVal2 !
你可以从列表的后面开始,然后走到前面,尽管如果你删除了相同的值,你仍然会有潜在的错误。
更短的方法是使用LINQ:var newList = lstTitles.Where(title=>!title.StartsWith('xxx'))
代替ToLower
,你应该使用StartsWith
的重载,它允许传递StringComparison.OrdinalIgnoreCase
。
然后使用List.RemoveAll
,这是最易读,最有效和最短的方法:
lstTitles.RemoveAll(s => s.TrimStart().StartsWith("x", StringComparison.OrdinalIgnoreCase));
我认为,你最好这样创建一个新的列表
list = list
.Where(i => ! i.StartsWith("xxx", StringComparison.InvariantCultureIgnoreCase))
.ToList();
它的复杂度是O(n)然而,试图移除1 × 1的复杂度是O(n^2)
也可以这样做:
list.RemoveAll(i => i.StartsWith("xxx", StringComparison.InvariantCultureIgnoreCase));
处理所有情况,不需要第二个列表。