将List<int>转换为List<int?>的最快方法是什么?

27
什么是将原始列表转换为可空原始列表的最快方法?例如:List<int> 转换为 List<int?>
简单的解决方案是创建一个新列表,并使用 foreach 循环添加每个项目,但这需要太多时间。

你认为什么是太长时间的定义?唯一的方法是循环(可以使用foreachLINQ)。 - psubsee2003
2
不是要聪明,但你能否稍微重构一下代码,这样就不必进行转换了? - bas
可能是将List<int?>隐式转换为List<int>的重复问题。 - Wesam
4个回答

60

没有比创建一个新列表更快的方法:

var newList = list.Select( i => (int?)i ).ToList();

然而,使用LINQ比使用裸循环慢。最快的方法是使用预先分配容量的List<int?>
List<int?> newList = new List<int?>(list.Count); // Allocate enough memory for all items
foreach (var i in list)
    newList.Add(i);

如果您想要对列表项进行就地类型更改,这是不可能的。


1
不使用LINQ会更快。 - Jeff Mercado
@JeffMercado 实际上,我同意 :-) 我没有测量性能差异,但我猜它是可以忽略不计的。 - Mohammad Dehghan
3
有趣。我的测试结果也显示,一个简单的循环比使用LINQ(处理100万项数据)快30%-40%。 - Mohammad Dehghan
你真的知道迭代遍历项目会比一些像 Cast 方法这样的东西表现更好吗?! :)) 我敢打赌你以前没有测量过! - Arman Ebrahimpour
1
@ArmanEbrahimpour 我测量了。这个答案是7年前的。我知道LINQ运算符会增加一些开销,但我认为它可以忽略不计。现在我了解了所有的开销以及编译器和运行时优化可能会消除这些开销!记住,在进行优化之前,您应该始终进行测量! - Mohammad Dehghan

16

你可以使用 Cast LINQ 操作符而不是 Select

List<int> first = new List<int>() {1, 2, 3};
List<int?> second = first.Cast<int?>().ToList();

Select 更慢 - Avestura
2
你能证明吗? - Matten
请参见本问题下的另一条答案。 - Avestura

8

如果你想知道更快的解决方案,你应该通过使用三种不同的方法进行一些基准测试:基准测试

List<int> list = Enumerable.Range( 0, 10000 ).ToList( );
Stopwatch sw = Stopwatch.StartNew( );

for ( int i = 0; i < 100000; i++ ) {
   List<int?> newList = new List<int?>( );
   foreach( int integer in list )
      newList.Add( ( int? ) integer );
}

sw.Stop( );
TimeSpan timespan = sw.Elapsed;
Console.WriteLine( String.Format( "Foreach: {0:00}:{1:00}:{2:00}", timespan.Minutes, timespan.Seconds, timespan.Milliseconds / 10 ) );
sw.Restart( );

for ( int i = 0; i < 100000; i++ ){
   List<int?> newList = list.Select( x => ( int? ) x ).ToList( );
}

sw.Stop( );
timespan = sw.Elapsed;
Console.WriteLine( String.Format( "LINQ-Select: {0:00}:{1:00}:{2:00}", timespan.Minutes, timespan.Seconds, timespan.Milliseconds / 10 ) );
sw.Restart( );

for ( int i = 0; i < 100000; i++ ){
   List<int?> newList = list.Cast<int?>( ).ToList( );
}

sw.Stop();
timespan = sw.Elapsed;
Console.WriteLine( String.Format( "LINQ-Cast: {0:00}:{1:00}:{2:00}", timespan.Minutes, timespan.Seconds, timespan.Milliseconds / 10 ) );

结果:

基准测试

正如我们所预料的那样,最好的方法是第一种解决方案(foreach),这意味着循环遍历元素,进行转换并将其添加到新列表中。


1
你尝试过不使用foreach,而是使用简单的“for(i = 0; i < list.Count(); i ++)”吗?就像这个链接中所述:https://dev59.com/knRC5IYBdhLWcg3wP-dh - EGOrecords
有趣的事实是,我提供的SO答案有点过时了 ;) - EGOrecords
1
不要忘记在foreach循环中设置列表的容量,以使其更快、更精简。 - Hans Passant

0

这不是一个新问题,但我目前正在使用这段代码;我分享它的目的是希望它能帮助其他有同样问题的人: 预分配列表 + Linq:

var branchIds = new List<int?>(branches.Count);
branchIds.AddRange(branches.Select(int.Parse).Select(brId => (int?)brId));

你没有展示branches是什么,但由于你正在使用int.Parse进行转换,它必须是一些string集合,而问题是如何从List<int>创建一个List<int?> - Lance U. Matthews

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接