泛型 vs 数组列表

16

我所工作的系统是在 .net 2.0 发布之前编写的,并没有使用泛型。虽然最终升级到了2.0版本,但由于时间限制,代码没有进行重构。现有的代码中有很多地方使用ArrayList等以对象形式存储数据。

从性能角度来看,将代码改为使用泛型非常重要吗?我知道从性能角度来看,装箱和拆箱等操作效率低下,但实际上更改后能带来多少性能提升呢?使用泛型是不是应该在未来的开发中使用,或者它对性能的影响足够大,需要有意识地更新旧代码呢?

12个回答

14

从技术上讲,泛型的性能确实比较好。然而,除非性能非常重要并且您已经在其他方面进行了优化,否则通过在其他领域投入时间,您可能会获得更好的改进。

建议如下:

  • 以后使用泛型。
  • 如果您有稳定的单元测试,请在接触代码时将其重构为泛型。
  • 花费其他时间进行重构/测量,这将显着提高性能(数据库调用、更改数据结构等),而不是仅仅节省几毫秒。

当然,除了性能外,还有其他原因需要转换为泛型:

  • 较少出错,因为您可以在编译时检查类型。
  • 更易读,您不需要到处强制转换,很明显存储在集合中的类型是什么。
  • 如果您以后使用泛型,那么在任何地方使用它们更加干净。

8
这是我从一个100KB的文件中简单解析出来的字符串进行100,000次处理的结果。通用字符列表(Generic List(Of char))花费了612.293秒才能够100,000次浏览该文件。而ArrayList 花费了2,880.415秒才能够100,000次浏览该文件。这意味着在这种情况下(实际结果会有所不同),通用字符列表比ArrayList快4.7倍。
以下是我运行了100,000次的代码:
Public Sub Run(ByVal strToProcess As String) Implements IPerfStub.Run
    Dim genList As New ArrayList

    For Each ch As Char In strToProcess.ToCharArray
        genList.Add(ch)
    Next

    Dim dummy As New System.Text.StringBuilder()
    For i As Integer = 0 To genList.Count - 1
        dummy.Append(genList(i))
    Next

End Sub

 Public Sub Run(ByVal strToProcess As String) Implements IPerfStub.Run
     Dim genList As New List(Of Char)

     For Each ch As Char In strToProcess.ToCharArray
         genList.Add(ch)
     Next

     Dim dummy As New System.Text.StringBuilder()
     For i As Integer = 0 To genList.Count - 1
         dummy.Append(genList(i))
     Next
 End Sub

4
唯一确定的方法是使用像dotTrace这样的工具来分析您的代码。 http://www.jetbrains.com/profiler/ 在您的特定应用程序中,装箱/拆箱可能是微不足道的,不值得重构。但从现在开始,由于编译时类型安全性,您仍应考虑使用泛型。

3
泛型,无论是Java还是.NET,都应该用于设计和类型安全,而不是用于性能。自动装箱与泛型不同(基本上是隐式的对象到基元转换),正如您所提到的,如果有大量算术或其他操作将导致重复的隐式对象创建/销毁,则不应将它们用作基元的替代品。
总体而言,我建议只在需要清理以实现类型安全/设计目的而更新现有代码时,才使用前进,并避免过多考虑性能。

1

这要看情况而定,最好的答案是对你的代码进行分析。我喜欢使用AQTime,但也有其他一些工具可供选择。

一般来说,如果ArrayList被频繁使用,将其转换为泛型版本可能会更值得。但实际上,你很可能无法测量出性能差异。装箱和拆箱虽然是额外的步骤,但现代计算机速度非常快,几乎没有什么影响。由于ArrayList实际上只是一个带有良好封装的普通数组,因此你可能会从更好的数据结构选择中获得更多的性能提升(ArrayList.Remove的时间复杂度为O(n)!)而不是从转换为泛型中获得。

编辑:Outlaw Programmer提出了一个很好的观点,即使用泛型仍然需要装箱和拆箱,只是隐式地发生。不过,所有围绕着检查异常和空值的代码以及“is/as”关键字的代码都会有所帮助。


0

使用泛型应该意味着,如果您想在后来的C#版本中利用诸如Linq之类的东西,您的代码将更简单、更易于使用。


0

最大的收益将在维护阶段中体现。泛型更容易处理和更新,无需处理转换和强制转换问题。如果这是您经常访问的代码,请不要吝惜努力。如果这是多年未被触及的代码,我就不会太过烦恼。


0
自动装箱/拆箱与泛型有什么关系?这只是一个类型安全问题。在非泛型集合中,您需要显式地将对象强制转换回其实际类型。而使用泛型,您可以跳过此步骤。我认为这两种方法之间没有性能差异。

2
查看有关泛型及其在使用结构体时的实用性的文档,您将了解到装箱与此有何关系。(提示:您无法获取指向堆栈变量的对象指针) - Guvante

0

我的旧公司曾经考虑过这个问题。我们采取的方法是:如果重构容易,就去做;如果不容易(即会涉及太多类),就留到以后再说。这真的取决于你是否有时间去做,或者是否有更重要的任务需要完成(例如为客户实现功能)。

另一方面,如果你没有为客户工作,那就花时间重构吧。这将提高代码的可读性。


0

这取决于你的代码中有多少ArrayList。如果你在UI中绑定或显示大型列表,那么你可能会看到很大的性能提升。

如果你的ArrayList只是零散地分布在各处,那么将其清理掉可能不是什么大问题,但也不会对整体性能产生太大影响。

如果你在代码中使用了很多ArrayList,并且替换它们将是一项巨大的工作(可能会影响你的进度),那么你可以采用“如果你触及它,就改变它”的方法。

最重要的是,泛型更易于阅读,并且由于强类型化,它们在整个应用程序中更加稳定。你不仅会从性能上获得收益,还会从代码可维护性和稳定性方面受益。如果你能快速完成,我建议你去做。

如果你能得到产品负责人的支持,我建议你将其清理干净。之后你会更爱你的代码。


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