??xml version="1.0" encoding="utf-8" standalone="yes"?>无遮挡粉嫩小泬久久久久久久,精品久久一区二区,精品水蜜桃久久久久久久http://www.shnenglu.com/mzty/category/654.html<br/> <br/> <a href = "http://www.shnenglu.com/mzty/archive/2007/03/02/19109.html"><font size = 5 color ="#00FFFF">{C++ 基础}<font/></a> <a href = "http://www.shnenglu.com/mzty/archive/2007/08/13/29922.html"><font size = 5 color ="#00FFFF">{C++ 高}<font/></a> <a href = "http://www.shnenglu.com/mzty/archive/2007/04/16/22064.html"><font size = 5 color ="#00FFFF">{C#界面QC++核心法}<font/></a> <a href = "http://www.shnenglu.com/mzty/archive/2007/03/04/19163.html"><font size = 5 color ="#00FFFF">{设计模式}<font/></a> <a href = " http://www.shnenglu.com/mzty/archive/2007/03/04/19167.html"><font size = 5 color ="#FF0000">{C#基础}<font/></a> zh-cnTue, 20 May 2008 16:57:19 GMTTue, 20 May 2008 16:57:19 GMT60Make Your Apps Fly with the New Enterprise Performance Tool (通过新的 Enterprise Performance Tool 使应用程序飞速运?(包含各种排序法的实?http://www.shnenglu.com/mzty/archive/2006/04/27/6389.html梦在天梦在天Thu, 27 Apr 2006 09:46:00 GMThttp://www.shnenglu.com/mzty/archive/2006/04/27/6389.htmlhttp://www.shnenglu.com/mzty/comments/6389.htmlhttp://www.shnenglu.com/mzty/archive/2006/04/27/6389.html#Feedback0http://www.shnenglu.com/mzty/comments/commentRss/6389.htmlhttp://www.shnenglu.com/mzty/services/trackbacks/6389.htmlhttp://msdn.microsoft.com/msdnmag/issues/04/12/EnterprisePerformance/default.aspx#contents


中文:http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/enterpriseperformance.mspx?mfr=true


发布日期Q?1/6/2005 | 更新日期Q?1/6/2005

John Robbins

本文Z Visual Studio 2005 的预发布版本。文中包含的所有信息均有可能变更?/p>

本文讨论Q?

?/td>

分析器的内部工作方式

?/td>

EPT 的灵zd?

?/td>

一个供分析的示例应用程?/p>

代码可从以下位置下蝲Q?/b>
EnterprisePerformance.exe (258KB)

快速代码仍然很受欢q。即使我用来键入本文的计机h_的能力和内存Q能够同时控制一座原子能发电厂、一个火星O游计划以及美国西部上I的IZ交通,q且仍然h充的能力来处理星际探烦中的 SETI 数据包,但这q不意味着开发h员不再需要担心其代码的速度和效率。在q去q行 Win32] 本机开发的日子里,我们不仅需要担心速度Q而且q要担心 PC q_上那些o厌的讉K冲突Q对于你们这些老家伙,q有“全局保护错误”和“不可恢复的应用E序错误”)。尽托代码已l消除了其中的一些担心,但它只意味着您所l历的那些性能问题可能比以前更加难以捉摸。主要原因是Q在使用托管代码Ӟ我们不具有在q行本机开发时所拥有的简便的q行库视图?/p>

有许多次Q当我正在用客LӞ我不知道如何解决恶性的性能问题。当Ӟq些性能问题不会出现在Q何测试系l中Q它们只会出现在真实世界的生产中。由于公paq行?(CLR) 是黑盒,因此如果我希望找到在试pȝ中重复性能问题的方法,则很N会发生什么事情。尽在市场中有一些第三方商业性能工具Q但q些工具中的大多数都会对pȝ造成q多的干扎ͼ以至于根本不能考虑在生产系l中使用。这也就是当我看?Microsoft 提供一个全新的分析??Enterprise Performance Tool (EPT) 以作?Visual Studio] 2005 Team Developer Edition 的一部分Ӟ感到如此兴奋的原因。它是我可以真正考虑在生产系l中使用的第一个分析系l,因ؓ它提供了一些非常轻便的攉性能数据的手Dc因为我曄领导q一U最畅销的商业分析器的开发工作,所以我能够理解在不产生太多pȝ开销的情况下攉有用分析数据的困隄度?/p>

在本文中Q我介l?EPT 的基本原理,q向您说明如何开始用它。因为分析器所h的复杂性,所以在来某一期中Q我讨论如何?EPT 来跟t您可能在同事的代码中遇到的实际性能问题Q我知道您的代码非常完美Q)。请CQEPT 正处在测试阶D(我用的?Burton Beta 1 h位版?40607.83Q,q且在该产品发布之前Q可能会?UI 或某些特定步骤进行更攏V在?EPT q行介绍之前Q我希望q儿时间谈Z下分析器通常是如何工作的Q以便您可以更好C解是什么 Enterprise Performance Tool 变得如此与众不同?/p>


分析器的基本原理

在您~写分析器时Q可以选择两种攉数据的方式中的一U:探测和采栗这两种方式都十分有效,但是每种方式都有它的折衷Ҏ。探分析器攉数据的方式是在应用程序中插入探测或挂钩,以便在程序执行该挂钩时就调用分析器运行库。要攄探测Q分析器需要在~译步骤中将应用E序仪表化,重写已经~译的二q制文gQ或者即时将应用E序仪表化。要查看Z .NET 的应用程序的探测分析器方法示例,请阅?Aleksandr Mikunov 的一非常出色的文章 —?a target="_blank">Rewrite MSIL Code On the Fly with the .NET Framework Profiling API”(该文章摘?MSDN]Magazine 2003 q?9 月刊Q。当我开始讨?EPT 的时候,您将看到它用术语“A表化”来表示探测Ҏ?/p>

探测分析器方法的主要优势在于Q当应用E序执行Ӟ始l调用所插入的探。这P分析器运行库对q行h完整的认识,因此在生成关键信息(例如Q函C间的父子关系Q时可以保正确Qƈ且分析器可以报告完美的调用树Q以便您可以L扑ֈp最长时间的调用路径。用探分析器Ӟ没有什么事情能够阻止开发h员只在函数入口和出口处插入探。可以在源代码行U别攄额外的探,以便您对函数h完整的认识?/p>

但是Q探分析器所提供的详l视囑օ有一些缺炏V第一个缺Ҏ仪表化方案用v来可能很ȝQƈ且因为它是在二进制别重写,因此存在很多可能引入潜在错误的领域。正如您可以惛_到的那样Q这些探还占用了空_从而导致一些代码膨胀和较低的性能。对于完全A表化的应用程序,探测分析器可能会D速度大幅度下降,以至于几乎不可能在生产系l上q行仪表化的二进制文Ӟ从而您在最需要该分析器的时候却无法利用它?/p>

正如其名U所暗示的那P采样分析器按照预先定义的旉间隔获得应用E序中正在执行的操作的快照。大多数开发h员都没有意识?Microsoft L在他们的开发环境中随附了一个采样分析器。它被称试器Q?如果您开始调试应用程序,q且每隔 30 U左叛_中断臌试器Q则您可以注意到应用E序U程正在何处执行Q以便很好地了解应用E序在一ơ运行过E中执行了哪些操作。我已经通过手动完成采样分析器的工作Q解决了很多生性能问题?/p>

佉K样分析器如此有h值的原因在于Q它们具有比探测分析器小得多的系l开销。这意味着Q您可以有更高的Z在生产系l中使用它们Q而不会服务器疲于奔命以至于停机。采样分析器的问题在于,从应用程序中获得的所有样本很有可能根本不昄M代码。例如,如果您具有大量用数据库的应用程序,则所有样本都可以来自数据库访问程序集的内部。最后,只抓取每个线E的当前执行指o的传l采样分析器会得确定方法之间的父子关系变得十分困难Q因而确定性能最差的执行代码途径要困隑־多?/p>


Enterprise Performance Tool 的基本原?/p>

在了解分析器的操作方式之后,我就可以讨论 EPT 所采取的方式了。简单地_它既是采样分析器Q又是探分析器QMicrosoft UC为“A表化”)。其思想是,您在开始时通过采样分析器来查看应用E序性能Q以获得常规性能特征Q以便您可以开始将注意力集中于应用E序的热炚w题上。在您了解具有一些问题的E序集之后,可以求助于仪表化分析以查看特定的问题领域,以便修复它们。当Ӟ如果您要执行单元性能试Q则没有什么能够阻止您直接转到对特定模块进行A表化Q以便在聚焦Ҏ中查看它们的性能?/p>

?EPT 采样分析器有的部分原因在于Q您可以使用大量目来触发样本。默认的采样Ҏ旉周期Qƈ且可能是您L使用的采L。默认情况下Q每一百万个时钟周期采样一ơ,但是您可以将采样间隔的时钟周期数更改为您喜欢的Q何|可是该D,EPT 所D的系l开销p大。对于生产服务器Q您可以该数字讄为某个非帔R的数字(如五百万Q,以ɾpȝ开销保持在合理的水^Q同时不会完全破坏进E中的可用性。正如您预料的那P每五百万个时钟周期采样一ơ将意味着您需要应用E序q行相当长的旉Q以便在您的热点区域中获得良好的h分布?/p>

如果您的应用E序使用了很多内存,则可以选择?EPT 采样分析器改为在出现错误时触发。这P您就可以在数据被交换?RAM 时获得性能快照Qƈ且可以看到是谁在执行推送操作。如果初始分析器q行表明您在代码外部的区域中p了大量时_则可以告诉分析器改ؓZpȝ调用来完成采栗如果您要分析具有大量线E的多线E应用程序,则该采样l计信息会对您在从用h式{换到内核模式Q这表明某些U程可能会不必要地在内核对象上阻塞)时的数据q行拍照。您可以用于采样触发器的最后一些值是 CPU 所支持的各U性能计数器,例如Q分支计数或~存丢失。这是一个只有极数人才实需要的高选项Q但是如果您实需要该数据Q那么知道该数据存在也不错?/p>

那些忙碌?Redmontonian q解决了调用堆栈问题 ?q是Ҏ用的采样分析器造成障碍的最大问题之一。正如我在前面提到的那样Q大多数采样分析器在采样时只是对当前正在执行的指令进行拍照。Microsoft 解决了如何将极快的堆栈遍历结合到他们的采样分析器部分中,以便您能够获得样本的好处Qƈ且知道在执行该样本时是如何到N里的。这使得这些快照与代码重新兌变得更加Ҏ?/p>

在讨论您可以分析的应用程序之前,我想提几件您很可能觉得有的事情。第一件事情是Q如果您认ؓ Microsoft 是从头开发该性能工具的,那么您只猜对了一半。在 Microsoft 内部Q开发团队一直在使用 EPT 的前w(名ؓ Call Attribute Profiler (CAP)Q它使用仪表化)?Low Overhead Profiler (LOP) ?一个采样分析器。由?Microsoft 开发了q些工具以收集有兛_用程序(例如Q操作系l和整个 Office 套gQ的性能信息Q因此它们甚至不会给您的应用E序带来什么负担。我曄使用q?EPT 的前w,所以我可以告诉您公q本用v来会Ҏ多少。此外,它们q具有一些极为有的功能Q稍后我予以讨论)?/p>

W二个有的事项?EPT 所支持的技术有兟뀂尽某些h可能认ؓ׃ Microsoft 非常偏重?.NET FrameworkQ因此无法将 EPT 用于 Win32 应用E序或本Z码,?EPT 团队实际上已l承诺支持所有的 Win32 本机应用E序以及 .NET 代码。这意味着Q无论您使用哪种技术(ASP.NET、Windows] H体、MFC ?Win32 服务Q,您都h完全的采样和仪表化支持。您看刎ͼ?Visual Studio .NET 中,跨技术?EPT 没有M差异?/p>

实际?EPT 讄非常q_Q只需?Visual Studio .NET 安装E序的“Enterprise Tools”树控g中选择“Enterprise Performance Tool”即可。当Ӟ因ؓ您知?EPT 仍然是一个测试品,所以您的第一个反应可能是q行虚拟 PCQƈ在那里安全地包含所有内宏V但是,Z执行采样分析QEPT 使用内核模式讑֤驱动E序来响?CPU 性能计数器中断,不过令h遗憾的是Q虚?PC 没有实现计数器。它也没有模拟高U可~程中断控制?(APIC)Q而这两者都是内核设备驱动程序完成其工作所必需的。好消息是,如果您没有额外的计算Z便安?EPTQ那么您也ƈ非完全不q,因ؓ仪表分析器仍然能够工作。如果您没有多余的计机以便安装 EPTQ那么这是一个让公司为您购买另一台计机的好借口?/p>


Animated Algorithm

要学习Q何工L用法Q您都需要一个合适的CZ应用E序Q以便能够最佛_利用该工兗在试周期的这一时刻QEPT 没有随附MCZQ但是在我的盘上已l有了一个完的分析器示例。早些时候,我正在尝试解军_何在 Windows H体应用E序中用多U程的问题,因此我编写了一个名?Animated Algorithm 的了不v的小E序Q该E序可实时激zd量排序算法?b>?1 昄我的CZ应用E序已经准备好排序?/p>


?1 正在工作?Animated Algorithm

Animated Algorithm 使您可以在窗体的l合框中Q从 15 个不同的排序法中进行选择。“Options”菜单您可以选择各个元素交换或设|之间的休眠旉Q以便您可以降低囑Ş更新的速度?/p>

我不久前使用 Microsoft] .NET Framework 版本 1.1 ~写?Animated AlgorithmQ因此您不会在代码中扑ֈM奇特的泛型或新的 BackgroundWorker VNSORT E序集中的排序算法来自由 Jonathan de Halleux、Marc Clifton ?Robert Rohde 张脓?The Code Project 上的一优U文章Q请参阅 Sorting Algorithms In C#Q,该程序集算法封装到公共l构中,以便您可以轻村֜替换执行元素交换和设|的cR因为它们具有非常好的体pȝ构,所以我需要关心的所有内容ؓ UI 部分?/p>

在本文的其余部分中,我将分析 Animated Algorithm E序。如?EPT 团队该E序作ؓCZ应用E序随附在品中Q则会非常棒。(哈哈。)


EPT 入门

?Visual Studio 2005 Beta 1 中,在哪里可以找?EPT 当然是不明显的。EPT 在您启动 Performance WizardQ它位于“Tools”菜单下Q时启动Qƈ且无论是否打开目Q它都存在。请CQPerformance Wizard 所创徏的性能会话不是目的一部分Q它们实际上是具有自q IDE H口Q称?Performance ExplorerQ的单独文g。您可以通过从“File”|“Open”对话框中选择 PSESS 文gQ来打开您创建的性能会话?/p>

如果您在单步执行 Performance Wizard 时没有打开目Q则所产生的性能会话与您指定的二进制文件相兌。但是,在测试版中,在您指定要运行的二进制文件时Q必L开兌的项目。我只是想顺便提一下这个小的技巧,因ؓ当我W一ơ遇到该问题Ӟ它确实让我困惑不巌Ӏ?/p>

在您启动 Performance Wizard 以后Q呈现在您面前的W一个屏q要求您选择要分析的应用E序。如果您打开了一个可生成多个E序集的目Q如 Animated AlgorithmQ,则只能从该向g选取一个程序集。如果要q行采样Q则只选取q一个程序集是很好的Q因?EPT 采样会分析加载的所有程序集Q包括那些来自框架类库的E序集)。但是,如果您要对多个程序集执行仪表化分析,?Performance Wizard 只选择q一个程序集Q因此您需要在 Performance Explorer 中所生成的性能会话中指定其他项目或E序集。稍后我向您说明如何完成该工作?/p>

在选择了要在性能会话中用的E序集或目之后Q您必须选取分析Ҏ。在 Performance Explorer 中的M位置Q您都可以在采样和A表化之间切换Q以满自己的需要;您在该向导页中进行的选择只表C您最初希望执行的操作。在选择了分析方法之后,向导基本完成了。对?EPT 的最l版本,您将?Performance Wizard 中具有用于指定附加信息的更多选项。最l版本还您可以直接从 Performance Explorer 中创建性能会话?/p>

?2 昄?Performance Explorer 在刚刚完?Performance Wizard 步骤以创?AnimatedAlgorithms 目的A表化q行之后的窗口。要d另一个项目的输出二进制文Ӟ请右键单几ZTargets”文件夹Q然后从上下文菜单中选择“Add Target Project”。如果要d与该目没有兌的特定二q制文gQ请选择另一个选项 —“Add Target Binary”。如果您已经选择了“Add Target Project”,则可以在产生的对话框中从已打开的解x案中选择其他目?/p>


?2 Performance Explorer

如果您已l选择了A表化q行Q它q色启动箭头下面的下拉列表框中的文本表C)Q则二进制文件A表化在E序执行之前发生。如果您不希望针对运行A表化某个特定的二q制文gQ则请右键单击该二进制文Ӟq取消选中“Instrument Binary”菜单选项?/p>

如果您已l选择了采样分析,q且希望附加到某个正在运行的目Q则单击“Attach/Detach”按钮(“Start”按钮右侧的斜向头Q将呈现“Attach Profiler to Process”对话框。通过 EPTQ您可以Ҏ需要附加到L多的q程Q以便获得对应用E序的认识。“Attach Profiler to Process”对话框q允许您从特定的二进制文件中分离分析。在来的某一?MSDN Magazine 中,我将更详l地讨论如何附加到现有的q程Q特别是Zq行 ASP.NET 性能调整Q?/p>

Performance Explorer H口剙的最后一个按钮是无所不在的“Properties”按钮。在启动分析q行之前Q您可能希望览一下性能会话属性,以设|几个关键属性。第一个属性位于“General”选项卡上Q它是您希望为性能会话存储性能报告的位|。在分析目Ӟ默认讄是将报告存储在与解决Ҏ相同的目录中。但更好的做法是性能会话和它们的相应报告攄在它们自q目录中,以便您可以更Ҏ地存储特定的q行集。这栯可以更容易地分析之前和之后的情况Q以便查看您所q行的代码更改的影响?/p>

在“General”选项卡上Q您q可以在仪表化和采样分析之间切换Q这会更改在 Performance Explorer 中显C的|。在我进行的性能调整中,我喜Ƣ将特定的会话专用于单个cd的分析,以避免出C报告有关的؜淆。没有Q何事情阻止您为所有种cȝ特定ҎQ涵盖从分析cd到单个二q制文g仪表化的所有方案)创徏C百计的不同性能会话文g。我q将提一下“General”选项卡上的最后一个项Q它h一个非常诱人的名称 —“Managed Allocation Profiling”,怿q会使您感到更加好奇。在我讨论完常规分析之后Q我返回到该项?/p>

“Performance Session”属性页上的另一个有的选项卡是“Sampling”选项卡(请参?b>?3Q。在q里Q您可以告诉 EPT 您要执行哪种cd的采栗正如我在前面提到的那样Q您对于希望如何q行采样h非常好的控制?/p>


?3 各种 EPT 采样计数器选项

在执行分析运行时QEPT 会在二进制文件在盘中所处的位置上将其A表化。如果您希望A表化的二q制文gUd到另一个位|,请选择“Performance Session”属性页中的“Binary”选项卡,然后选中“Relocate Instrumented Binaries”(它与 REBASE 样式的重定位l对没有M关系Q,q且指定您希望将更改后的二进制文件移至何处?/p>

“Instrumentation”选项卡您可以指定希望在仪表化发生之前和之后q行的程序。如果您需要对仪表化的二进制文件执行其他Q务(例如Q将其移动到全局E序集缓存中?Web 服务器上的特定位|)Q则该选项卡可能很有用。“Advanced”选项卡在该测试版中未公开。最后,通过“Counters”选项卡,您可以告?EPT 从系l的 CPU 中收集其他数据,例如QL2 ?L3 ~存d不中。显Ӟq些选项是只有少数开发h员才会需要的非常高的选项Q但是如果您实需要它们,那么它们可以发挥巨大的作用?/p>

在我l箋讨论查看采样数据之前Q我x一下,“Performance Explorer”窗口可以根据您的需要打开L多个性能会话。当您希望观察特定的前后ҎQ或者希望用不同的A表化二进制文件执行单独的试q行Ӟq一Ҏ为有用。当您打开多个性能会话Ӟ应当保右键单击特定的会话,选择“Set as Current Session”以便让该会话的讄执行Q然后将报告归档到它的报告节点中?/p>


查看分析器数?/p>

性能会话讄为您希望执行的操作以后,可以启动分析了。我首先对 Animated Algorithm 执行采样分析Q以查看我是否可以找C些热炏V从采样中获得良好数据的关键在于执行较长旉的运行。对?Animated AlgorithmQ我会将 15 个排序算法中的每一个算法运行两ơ,q将采样讄为默认的一百万个时钟周期?/p>

在完成某个运行之后,EPT 会将该运行的报告攑ֈ性能会话的“Reports”文件夹中。EPT 在运行期间收集原始性能数据Qƈ其式传输到报告文件中Q不做Q何分析)。这P您可以在q行应用E序旉免所有系l开销Q但是您ؓ大型报告文g付出代h。我刚才完成的运行的采样报告文g大小?3.70MBQ它用了大约三分钟才完成。请保您在q行 EPT 时具有大量的盘I间?/p>

所有数据分析(它必然伴有调用堆栈的生成以及性能数字的计)都在您打开报告文g时发生。对于测试版Q在打开文g旉度可能会降低。看h视图好像处于无限循环中,但是Q如果进度栏正在报告H口中移动,那么h耐心一些,文g最l将弹出?/p>

M分析q行中的W一个视图是“Performance Report Summary”,它显C在刚刚完成?Animated Algorithm 采样q行?b>?4 中。不出所料,采样发生在整个应用E序中,因此您正在查看的信息也就是您在应用E序中看到的内容Q大部分工作都发生在框架cd或操作系l内部。如果您实在采样“Summary”视图中看到了您的一个方法,则您很可能看C一个性能问题?/p>


?4 EPT 采样性能报告摘要

快速浏览一?b>?4Q您可能想知?Inclusive Sampled ?Exclusive Sampled 之间的区别。Exclusive Sampled 意味着该方法在取样时位于堆栈的剙。换句话_它是当前正在执行的函数。Inclusive Sampled 意味着该函数在取样时出现在调用堆栈中。因而,包含Ҏ是当前正在执行的Ҏ的调用方?/p>

在采h案中Q一个方法在调用堆栈 (Inclusive Sampled) 中出现的ơ数多Q该函数在执行中p的时间就多Q因此这里是您需要重点关注以q行性能调整的地斏V对?Exclusive Sampled 函数而言Q函数在那里频繁出现表明该函数正在被频繁地调用,但是它的执行实际上可能非常快速。对于像 Animated Algorithm q样需要进行大量图形处理的应用E序Q我完全能够预料?GDIPLUS.DLL 中的某个函数靠q刚刚显C的列表的顶部。在?4 中,位于 GDIPLUS.DLL 中偏U量 0x5B8D 处的函数Q它恰好?FLOOR 函数Q被一直调用,以便计算在屏q上的哪个位|显C某些内宏V当您观察性能q行ӞL保设|符h务器以获得可能存在的最佳信息。在撰写本文Ӟ我用了 EPT 的未发布版本Q因而符号尚不可用?/p>

在我跛_其他视图中以前,我希望A表化 Animated AlgorithmQƈ且完成与我针寚w样分析器完成的运行相同的q行Q以便显CZA表化q行的性能报告摘要。正如您可以猜到的那P仪表化的q行会生成比采样q行多得多的数据。对于该q行Q我仪表化了 Animated Algorithm 中的全部五个E序集,q最l得C?375MB 大小的会话文件?/p>

采样和A表化数据之间的主要区别是Q采h看整个进E空_q且显C框架类库或操作pȝ内部Q换句话_是您在其中不具有源代码的位|)的调用。另一斚wQA表化只查看应用程序以及您在非仪表化模块上直接调用的方法。例如,如果您具有一个“Hello World!”应用程序,q且它的 Main 只调?Console.WriteLineQ则您将获得 Main 中Q何工作的计时信息以及 Console.WriteLine 长度的计时信息,但是您不会获得有?Console.WriteLine Ҏ的Q何详l信息?/p>

?5 昄了A表化q行的性能报告摘要。第一个表“Most Called Functions”显CZ频繁使用的函数。该表中的第一列被错误标记为时_它实际上表示对该函数的调用次数。百分比列显CZ对该特定函数q行的调用L数所占的癑ֈ比。在大多数运行中Q您在q里看到框架cd或操作系l函数。如果您看到一些来自您自己的代码的函数Q则您最好了解一下您Z么如此频J地调用该特定函数?/p>


?5 仪表化运行的摘要

“Functions with Most Individual Work”表列出了那些花费大部分旉以仅仅执行该函数Q没有Q何其他函数调用)的方法。这也称函数的独占时间。对于测试版本,“Time”列的单位ؓ旉走格数。对于最l版本,单位是毫秒。但是,我认为性能q行的实际原始单位对于分析没有用。最重要的数字是癑ֈ比。在观察性能问题Ӟ您希望知道,与应用程序中的所有其他方法相比,哪个Ҏ占用了最长的旉。您在观察像 3519639455 ?3492589504 q样的两个数字时Q很隑֯它们q行什么比较。幸q的是,该表包含癑ֈ比,而我?EPT 团队的徏议是从图表中丢弃原始数据?/p>

最后一个表“Functions Taking Longest”显C方法的实际旉Q也UCؓ跑表旉或运行时_。分析器记录Ҏ的入口点旉和出口点旉Qƈ这两个值相减。该数字늛了被调用的所有子Ҏ、所有上下文切换以及该方法执行的休眠。在?5 中,您可以看?System.Windows.Forms.Application.Run 占用了最长时_像您对 Windows H体应用E序所预料的那栗尽很多开发h员将注意力集中于独占旉Q但q只是整个性能状况的一部分。如果方法正在对数据库进行调用或者进?Web 服务调用Q则您的Ҏ在运行时所在的U程在{待q些调用q回数据旉塞,从而得该U程?CPU 中被U走。通过密切xҎ的运行时_您可以找C码中正在降低应用E序q行速度的部分?/p>

管摘要视图很不错,但您最感兴的是查看代码在何处阻塞了pȝ的其余部分(对于采样q行而言Q,或者阻塞了应用E序的其他方法(对于仪表化运行而言Q。这是“Function”视囄职责范围 ?通过单击报告H口底部的“Function”按钮可以选择该视图。您q可以双几ZSummary”视囄MҎ以蟩至“Function”视图?/p>

对于采样q行Q“Function”视图显CZ臛_一个包含捕获中所有函数捕L列表。对于A表化q行Q您看Cq行的一部分调用的所有A表化Ҏ。无论您正在执行哪种cd的分析,都会在“Function”视图中昄很多数据Q因此您可以对代码的状况有一点儿感觉?/p>

默认情况下,采样“Function”视图显C“Inclusive Samples”列和“Exclusive Samples”列。由于我喜欢癑ֈ比数字,因此我右键单M列标题以向列标题中添加“Inclusive Percent”和“Exclusive Percent”。如果您要对多进E系l进行采P则可能希望包含其他列Q例如,“Process Name”或“Process ID”)Q以便您可以标识哪个Ҏ采样与哪个进E相配。您q可以在仪表化“Function”视图中讄列标题,但是您将h不同的标题组以供选择?/p>

在“Function”视图中分析采样q行Ӟ我喜Ƣ首先扫一眼“Function”视囄头几个按“Inclusive Samples”列排序的页Q以了解正在执行的方法。如果我在头几个中没有看到我的MҎQ则我会右键单击“Function”视囑ƈ选择“Group by Module”,以便获得树报告视图。当您将函数按模块分l时Q按特定列排序可以正执??q是一很不错的功能?/p>

对于仪表化运行,“Function”视囑օ有更多要昄的列。如果您拥有一?40 英寸的显C器Q则无需最大化 Visual Studio .NET H口应当能够看到所有这些列。对于我们中的其他h而言Q查看“Function”视囄最x式是?Alt + Shift + Enter 以切换到全屏q模式?/p>

在这些列中,“Function”视图中的A表化q行使用我在前面解释q的“包含”和“独占”术语。但是,q有另一个人؜淆的术语Q应用程序。正如我提到的那Pq行旉是从一个A表化点到另一个A表化点的L_而不该U程可能q行了哪些上下文切换。应用程序时间的思想?EPT 提取出在这些上下文切换中所p的时_以便您可以看到您的代码在 CPU 中实际执行的旉?/b>?6 列出了您在仪表化“Function”视图中看到的不那么明显的列的定义。您可能希望它传送到昄器上Q直?EPT 的联机帮助问世?/p>

在观察A表化q行的“Function”视图时Q我d了这些列以查看各U计时的癑ֈ比|U除原始数字旉列,q且d了两个{换列。这为我提供了有兌q行的更清晰视图。我在排序时所依据的第一个列是? Application Exclusive Time”,因ؓ我希望看到哪个函数正在完成大部分工作。由于A表化在方法进行的所有子调用周围攑օ了探,所以您完全有可能在该列表的剙看到框架cd或操作系l。实际上Q对于我?Animated Algorithm q行QSystem.Drawing.SolidBrush.ctor ?System.Drawing.Brush.Dispose ?Application Exclusive Time 癑ֈ比中被列为第一和第二,其百分比分别?14.982% ?14.867%。我~写的第一个函数是位于W三位的 Bugslayer.SortDisplayGraph.SorterGraph.UpdateSingleFixedElementQ其癑ֈ比ؓ 12.217%Q,它在囑Ş中绘制单独的条。根据应用程序类型的不同Q我在查看“Function”视图时可能会选择按其他列排序。如果存?Web 服务或数据库调用Q则我将查看 % Elapsed Inclusive TimeQ以便可以看到是否有特定Ҏ卷入到长旉d中。对于像 Animated Algorithm q样的应用程序,我还查?Application Inclusive Time 的百分比?/p>

Z我的仪表化运行中的上q数字,我很x明是谁在?SolidBrush Ҏq行q些调用Q因此我右键单击 .ctor Ҏq择“Show in Caller/Callee”视图,以便查看是谁在调用该Ҏ。该视图Q它对于采样分析也可用)使您一眼就可以看出目标Ҏ的所有调用方Q以及该目标Ҏ调用的所有方法?/p>

因ؓ .ctor Ҏ没有仪表化,所以“Caller/Callee”视囑ְ不会昄M被调用方Q但是它昄会显C用方。我双击了这个唯一的调用方Q它恰好是具有第三高 Application Exclusive Time q具?a target="_blank">?7 所C囄 UpdateSingleFixedElement Ҏ?/p>

?a target="_blank">?7 中,位于视图中部的下拉组合框是目标方法(在本例中?UpdateSingleFixedElementQ。方法上方的|格包含了目标方法的所有调用方Q调用方Q。目标方法下方的|格包含了目标方法调用以完成其工作的所有方法(被调用方Q。如果您希望查看是谁调用了特定调用方Q请双击该调用方ҎQ该Ҏ变为目标方法,q且您将看到原始目标Ҏ下降到被调用斚w分中。实质上Q您只是堆栈遍历了一遍?/p>

仅仅Z?7 中的视图Q您可以L别出潜在的性能问题。Animated Algorithm g不具有Q何突出的性能问题Q但?SolidBrush .ctor ?Dispose 占用了如此多的时间ƈ且都?UpdateSingleFixedElement Ҏ内部调用Q调用了 351,872 ơ)Q这个事实表明我做了一件愚蠢的事情 ?我每ơ都通过该函数创建画W,而实际上应该其~存。当我在来的某一?MSDN Magazine 中开始用 EPT 分析代码Ӟ您还看?Animated Algorithm 的其他一些问题?/p>

数据的最后一个常用视图是“Callstack”视图。在q里Q您可以通过更具层次性的方式看到您在“Caller/Callee”窗口中观察到的调用堆栈。对于采栯行,您将在“Callstack”视囄层看到很多的条目,因ؓq些条目中的每一个都代表一个包含独占样本的唯一炏V当您在采样q行中展开ҎQ您q将看到Q在相同U别偶尔会存在一些项Q这些项指示位于栚w的函数具有多个引向它的调用树。根位置中显C的Ҏ栈顶?/p>

对于仪表化运行,“Callstack”窗口将h与应用程序中的每个线E相对应的根元素。因?Animated Algorithm 只有两个U程Q所以您只能在树根别看C个项。在“Callstack”视图中Q您可以看到l对调用堆栈Q从仪表化的W一个方法向下到最后一个方法)Q因此您可以真正了解应用E序的执行方式。我已经有很多次Ҏ认ؓ代码所完成的工作和代码实际上完成的工作之间的差异感到吃惊?/p>

您可以花费大量时间在“Callstack”窗口中分析代码。当通过应用E序观察特定的踪qҎQ您可以通过选择感兴的特定节点Q向下移动,右键单击Qƈ选择“Set Root”菜单选项Q来消除大量噪音。在?8 中,我希望查?NSort.SwapSorter.Sort q行的所有调用,因此它讄为根可以消除 UI U程的媄响?/p>

在将来的某一期中Q我更详细地讨?EPT 昄区域中的最后两个选项卡:“Trace”和“Type”。在“Type”视图中Q您可以观察已经在应用程序中分配的对象。它在测试版中有效。当我在前面讨论性能会话属性时Q我提到q在“General”选项卡上有一个“Managed Allocation Profiling”部分。如果您选择“Allocations-only”单选按钮,?EPT 会填充“Type”视图。在试版中Q报告看hcM于其他许多工具中的报告,但是数据攉g不像在其他工具中那样h如此之多的系l开销。最后,要了?Enterprise Performance Tool 团队的想法以及有兌工具的更多信息,L保在 blogs.msdn.com/profiler 查看他们的网l日记?/p>

John Robbins ?Wintellect 的创始h之一Q该公司是一家专门致力于 Windows ?.NET Framework 的Y件咨询、教育和开发公司。他的最新著作是“Debugging Applications for Microsoft .NET and Microsoft Windows?Microsoft Press, 2003)。要联系 JohnQ请讉K www.wintellect.com?/p>



梦在天 2006-04-27 17:46 发表评论
]]>
数据l构法?--C++语言实现http://www.shnenglu.com/mzty/archive/2005/12/24/2060.html梦在天梦在天Sat, 24 Dec 2005 11:22:00 GMThttp://www.shnenglu.com/mzty/archive/2005/12/24/2060.htmlhttp://www.shnenglu.com/mzty/comments/2060.htmlhttp://www.shnenglu.com/mzty/archive/2005/12/24/2060.html#Feedback1http://www.shnenglu.com/mzty/comments/commentRss/2060.htmlhttp://www.shnenglu.com/mzty/services/trackbacks/2060.html阅读全文

梦在天 2005-12-24 19:22 发表评论
]]>
C#排序法大全 http://www.shnenglu.com/mzty/archive/2005/12/24/2057.html梦在天梦在天Sat, 24 Dec 2005 07:51:00 GMThttp://www.shnenglu.com/mzty/archive/2005/12/24/2057.htmlhttp://www.shnenglu.com/mzty/comments/2057.htmlhttp://www.shnenglu.com/mzty/archive/2005/12/24/2057.html#Feedback0http://www.shnenglu.com/mzty/comments/commentRss/2057.htmlhttp://www.shnenglu.com/mzty/services/trackbacks/2057.html C#排序法大全 土h 2004-7-21

一、冒泡排?Bubble)

using System;

namespace BubbleSorter
{
 public class BubbleSorter
 {
  public void Sort(int[] list)
  {
   int i,j,temp;
   bool done=false;
   j=1;
   while((j<list.Length)&&(!done))
   {
    done=true;
    for(i=0;i<list.Length-j;i++)
    {
     if(list[i]>list[i+1])
     {
     done=false;
     temp=list[i];
     list[i]=list[i+1];
     list[i+1]=temp;
     }
    }
   j++;
   }
  }
 }

 public class MainClass
 {
  public static void Main()
  {
   int[] iArrary=new int[]{1,5,13,6,10,55,99,2,87,12,34,75,33,47};
   BubbleSorter sh=new BubbleSorter();
   sh.Sort(iArrary);
   for(int m=0;m<iArrary.Length;m++)
   Console.Write("{0} ",iArrary[m]);
   Console.WriteLine();
  }
 }
}

二、选择排序(Selection)

using System;

namespace SelectionSorter
{
 public class SelectionSorter
 {
  private int min;
  public void Sort(int [] list)
  {
   for(int i=0;i<list.Length-1;i++)
   {
   min=i;
    for(int j=i+1;j<list.Length;j++)
    {
    if(list[j]<list[min])
    min=j;
    }
   int t=list[min];
   list[min]=list[i];
   list[i]=t;
   }
  }
 }

 public class MainClass
 {
  public static void Main()
  {
   int[] iArrary = new int[]{1,5,3,6,10,55,9,2,87,12,34,75,33,47};
   SelectionSorter ss=new SelectionSorter();
   ss.Sort(iArrary);
   for (int m=0;m<iArrary.Length;m++)
   Console.Write("{0} ",iArrary[m]);
   Console.WriteLine();
  }
 }
}

三、插入排?InsertionSorter)

using System;

namespace InsertionSorter
{
 public class InsertionSorter
 {
  public void Sort(int [] list)
  {
   for(int i=1;i<list.Length;i++)
   {
   int t=list[i];
   int j=i;
    while((j>0)&&(list[j-1]>t))
    {
    list[j]=list[j-1];
    --j;
    }
   list[j]=t;
   }
  }
 }

 public class MainClass
 {
  public static void Main()
  {
   int[] iArrary=new int[]{1,13,3,6,10,55,98,2,87,12,34,75,33,47};
   InsertionSorter ii=new InsertionSorter();
   ii.Sort(iArrary);
   for(int m=0;m<iArrary.Length;m++)
   Console.Write("{0}",iArrary[m]);
   Console.WriteLine();
  }
 }
}

四、希排?ShellSorter)

using System;

namespace ShellSorter
{
 public class ShellSorter
 {
  public void Sort(int [] list)
  {
  int inc;
  for(inc=1;inc<=list.Length/9;inc=3*inc+1);
   for(;inc>0;inc/=3)
   {
    for(int i=inc+1;i<=list.Length;i+=inc)
    {
    int t=list[i-1];
    int j=i;
     while((j>inc)&&(list[j-inc-1]>t))
     {
     list[j-1]=list[j-inc-1];
     j-=inc;
     }
    list[j-1]=t;
    }
   }
  }
 }

 public class MainClass
 {
  public static void Main()
  {
   int[] iArrary=new int[]{1,5,13,6,10,55,99,2,87,12,34,75,33,47};
   ShellSorter sh=new ShellSorter();
   sh.Sort(iArrary);
   for(int m=0;m<iArrary.Length;m++)
   Console.Write("{0} ",iArrary[m]);
   Console.WriteLine();
  }
 }



梦在天 2005-12-24 15:51 发表评论
]]>
数据l构~~二叉树和BSTs(?Q{Q?/title><link>http://www.shnenglu.com/mzty/archive/2005/12/24/2055.html</link><dc:creator>梦在天</dc:creator><author>梦在天</author><pubDate>Sat, 24 Dec 2005 07:39:00 GMT</pubDate><guid>http://www.shnenglu.com/mzty/archive/2005/12/24/2055.html</guid><wfw:comment>http://www.shnenglu.com/mzty/comments/2055.html</wfw:comment><comments>http://www.shnenglu.com/mzty/archive/2005/12/24/2055.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/mzty/comments/commentRss/2055.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/mzty/services/trackbacks/2055.html</trackback:ping><description><![CDATA[     摘要: 原文链接QPart3: Binary Trees and BSTs   本文?考察数据l构"pd文章的第三部分,讨论的是.Net Framework基类库没有包括的常用数据l构Q? 二叉树。就像线形排列数据的数组一P我们可以二叉树惌Z二维方式来存储数据。其中一U特D的二叉树,我们UCؓ二叉搜烦树(binary search treeQ,UCؓBSTQ它的数据搜索能力比一?..  <a href='http://www.shnenglu.com/mzty/archive/2005/12/24/2055.html'>阅读全文</a><img src ="http://www.shnenglu.com/mzty/aggbug/2055.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/mzty/" target="_blank">梦在天</a> 2005-12-24 15:39 <a href="http://www.shnenglu.com/mzty/archive/2005/12/24/2055.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据l构~~队列、堆栈和哈希表(二)http://www.shnenglu.com/mzty/archive/2005/12/24/2054.html梦在天梦在天Sat, 24 Dec 2005 07:38:00 GMThttp://www.shnenglu.com/mzty/archive/2005/12/24/2054.htmlhttp://www.shnenglu.com/mzty/comments/2054.htmlhttp://www.shnenglu.com/mzty/archive/2005/12/24/2054.html#Feedback0http://www.shnenglu.com/mzty/comments/commentRss/2054.htmlhttp://www.shnenglu.com/mzty/services/trackbacks/2054.html原文链接Q?SPAN lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-fareast-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">Part 2: The Queue, Stack, and Hashtable

本文?考察数据l构"pd文章的第二部分,考察了三U研I得最多的数据l构Q队列(Queue)Q堆栈(Stack)和哈希表QHashtable)。正如我们所知,Quenu和Stack其实一U特D的ArrayListQ提供大量不同类型的数据对象的存储,只不q访问这些元素的序受到了限制。Hashtable则提供了一U类数组Qarray-like)的数据抽象,它具有更灉|的烦引访问。数l需要通过序数q行索引Q而Hashtable允许通过M一U对象烦引数据项?/P>

目录Q?/P>

?/P>

“排队顺序”的工作q程

“反排队序”——堆栈数据结?/P>

序数索引限制

System.Collections.Hashtablec?/P>

l论

 

?/P>

在第一部分中,我们了解了什么是数据l构Q评C它们各自的性能Qƈ了解了选择何种数据l构对特定算法的影响。另外我们还了解q分析了数据l构的基知识Q介l了一U最常用的数据结构:数组?/P>

数组存储了同一cd的数据,q过序数q行索引。数l实际的值是存储在一D连l的内存I间中,因此d数组中特定的元素非常q速?/P>

因其h的同构性及定长性,.Net Framework基类库提供了ArrayList数据l构Q它可以存储不同cd的数据,q且不需要显式地指定长度。前文所qͼArrayList本质上是存储objectcd的数l,每次调用Add()Ҏ增加元素Q内部的object数组都要查边界,如果出Q数l会自动以倍数增加光度?/P>

W二部分Q我们将l箋考察两种cLl结构:Queue和Stack。和ArrayList怼Q他们也是一D늛ȝ内存块以存储不同cd的元素,然而在讉K数据Ӟ会受C定的限制?/P>

之后Q我们还深入了解Hashtable数据l构。有时侯Q我们可以把Hashtable看作杀一U关联数l(associative array)Q它同样是存储不同类型元素的集合Q但它可通过L对象Q例如string)来进行烦引,而非固定的序数?/P>

“排队顺序”的工作q程

如果你要创徏不同的服务,q种服务也就是通过多种资源以响应多U请求的E序Q那么当处理q些hӞ如何军_其响应的序成了创建服务的一大难题。通常解决的方案有两种Q?/P>

“排队顺序”原?/P>

“基于优先等U”的处理原则

当你在商店购物、银行取Ƅ时候,你需要排队等待服务。“排队顺序”原则规定排在前面的比后面的更早享受服务。而“基于优先等U”原则,则根据其优先{的高低决定服务顺序。例如在医院的急诊室,生命垂危的病Z比病情轻的更先接受医生的诊断Q而不用管是谁先到的?/P>

设想你需要构Z个服务来处理计算机所接受到的hQ由于收到的hq远过计算机处理的速度Q因此你需要将q些h按照他们递交的顺序依此放入到~冲Z?/P>

一U方案是使用ArrayListQ通过UCؓnextJobPos的整型变量来指定要执行的Q务在数组中的位置。当新的工作hq入Q我们就单用ArrayList的Add()Ҏ其d到ArrayList的末端。当你准备处理缓冲区的Q务时Q就通过nextJobPos得到该Q务在ArrayList的位|g获取该Q务,同时nextJobPos累加1。下面的E序实现该算法:

using System;
using System.Collections;
public class JobProcessing

{

   private static ArrayList jobs = new ArrayList();
   private static int nextJobPos = 0;
   public static void AddJob(string jobName)

   {
      jobs.Add(jobName);

   }  

   public static string GetNextJob()

   {

      if (nextJobPos > jobs.Count - 1)

         return "NO JOBS IN BUFFER";

      else

      {

         string jobName = (string) jobs[nextJobPos];

         nextJobPos++;

         return jobName;

      }

   }

  

   public static void Main()

   {

      AddJob("1");

      AddJob("2");

      Console.WriteLine(GetNextJob());

      AddJob("3");

Console.WriteLine(GetNextJob());

      Console.WriteLine(GetNextJob());

      Console.WriteLine(GetNextJob());

      Console.WriteLine(GetNextJob());

      AddJob("4");

      AddJob("5");

      Console.WriteLine(GetNextJob());

   }

}

 

输出l果如下Q?/P>

1

2

3

NO JOBS IN BUFFER

NO JOBS IN BUFFER

4

q种Ҏ单易懂,但效率却可怕得难以接受。因为,即是Q务被d到buffer中后立即被处理,ArrayList的长度仍然会随着d到buffer中的d而不断增加。假设我们从~冲区添加ƈU除一个Q务需要一U钟Q这意味一U钟内每调用AddJob()ҎQ就要调用一ơArrayList的Add()Ҏ。随着Add()Ҏ持箋不断的被调用QArrayList内部数组长度׃Ҏ需求持l不断的成倍增ѝ五分钟后,ArrayList的内部数l增加到?12个元素的长度Q这时缓冲区中却只有不到一个Q务而已。照q样的趋势发展,只要E序l箋q行Q工作Q务l进入,ArrayList的长度自然会l箋增长?/P>

出现如此荒谬可笑的结果,原因是已被处理过的旧d在缓冲区中的I间没有被回收。也x_当第一个Q务被d到缓冲区q被处理后,此时ArrayList的第一元素I间应该被再利用。想想上qC码的工作程Q当插入两个工作——AddJob("1")和AddJob("2")后——ArrayList的空间如图一所C:
图一Q执行前两行代码后的ArrayList

注意q里的ArrayList共有16个元素,因ؓArrayList初始化时默认的长度ؓ16。接下来Q调用GetNextJob()ҎQ移走第一个Q务,l果如图二:


图二Q调用GetNextJob()Ҏ后的ArrayList

当执行AddJob(??Ӟ我们需要添加新d到缓冲区。显ӞArrayList的第一元素I间Q烦引ؓ0Q被重新使用Q此时在0索引处放入了W三个Q务。不q别忘了Q当我们执行了AddJob(??后还执行了AddJob(??Q紧接着用调用了两次GetNextJob()Ҏ。如果我们把W三个Q务放?索引处,则第四个d会被攑ֈ索引2处,问题发生了。如图三Q?IMG height=136 src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/2-3.gif" width=450 border=0>
图三Q将d攑ֈ0索引Ӟ问题发生

现在调用GetNextJob()Q第二个d从缓冲中U走QnextJobPos指针指向索引2。因此,当再一ơ调用GetNextJob()ӞW四个Q务会先于W三个被U走Q这有悖于与我们的“排序顺序”原则?/P>

问题发生的症l在于ArrayList是以UŞ序体现d列表的。因此我们需要将CQ务添加到׃Q务的x以保证当前的处理序是正的。不何时到达ArrayList的末端,ArrayList都会成倍增ѝ如果生生未被用的元素Q则是因用了GetNextJob()Ҏ?/P>

解决之道是我们的ArrayList成环形。环形数l没有固定的L和终炏V在数组中,我们用变量来l护数组的v止点。环形数l如囑֛所C:


囑֛Q环形数l图C?/P>

在环形数l中QAddJob()ҎdCQ务到索引endPos处(译注QendPos一般称为尾指针Q,之后“递增”endPos倹{GetNextJob()Ҏ则根据头指针startPos获取dQƈ头指针指向nullQ且“递增”startPos倹{我之所以把“递增”两字加上引P是因里所说的“递增”不仅仅是将变量值加1那么单。ؓ什么我们不能简单地?呢?误虑q个例子Q当endPos{于15Ӟ如果endPos?Q则endPos{于16。此时调用AddJob()Q它试图去访问烦引ؓ16的元素,l果出现异常IndexOutofRangeException?/P>

事实上,当endPos{于15Ӟ应将endPos重置?。通过递增QincrementQ功能检查如果传递的变量值等于数l长度,则重|ؓ0。解x案是变量值对数组长度值求模(取余Q,increment()Ҏ的代码如下:

int increment(int variable)

{

  return (variable + 1) % theArray.Length;

}

注:取模操作W,如x % yQ得到的是x 除以 y后的余数。余数L? ?y-1之间?/P>

q种Ҏ好处是~冲区永q不会超q?6个元素空间。但是如果我们要d过16个元素空间的CQ务呢Q就象ArrayList的Add()Ҏ一P我们需要提供环形数l自增长的能力,以倍数增长数组的长度?/P>

System.Collection.Queuec?/P>

p我们刚才描述的那P我们需要提供一U数据结构,能够按照“排队顺序”的原则插入和移除元素项Qƈ能最大化的利用内存空_{案是使用数据l构Queue。在.Net Framework基类库中已经内徏了该cZ—System.Collections.QueuecR就象我们代码中的AddJob()和GetNextJob()ҎQQueuecL供了Enqueue()和Dequeue()Ҏ分别实现同样的功能?/P>

Queuecd内部建立了一个存放object对象的环形数l,q过head和tail变量指想该数l的头和。默认状态下QQueue初始化的定w?2Q我们也可以通过其构造函数自定义定w。既然Queue内徏的是object数组Q因此可以将Mcd的元素放入队列中?/P>

EnqueueQ)Ҏ首先判断queue中是否有_定w存放新元素。如果有Q则直接d元素Qƈ使烦引tail递增。在q里tail使用求模操作以保证tail不会过数组长度。如果空间不够,则queueҎ特定的增长因子扩充数l容量。增长因子默认gؓ2.0Q所以内部数l的长度会增加一倍。当然你也可以在构造函C自定义该增长因子?/P>

Dequeue()ҎҎhead索引q回当前元素。之后将head索引指向nullQ再“递增”head的倹{也怽只想知道当前头元素的|而不使其输出队列QdequeueQ出列)Q则QueuecL供了Peek()Ҏ?/P>

Queueq不象ArrayList那样可以随机讉KQ这一炚w帔R要。也是_在没有前两个元素出列之前,我们不能直接讉KW三个元素。(当然QQueuecL供了Contains()ҎQ它可以使你判断特定的值是否存在队列中。)如果你想随机的访问数据,那么你就不能使用Queueq种数据l构Q而只能用ArrayList。Queue最适合q种情况Q就是你只需要处理按照接收时的准顺序存攄元素V?/P>

注:你可以将QueuesUCؓFIFO数据l构。FIFO意ؓ先进先出QFirst In, First OutQ,其意{同于“排队顺序(First come, first servedQ”?/P>

译注Q在数据l构中,我们通常U队列ؓ先进先出数据l构Q而堆栈则为先q后出数据结构。然而本文没有用First in ,first out的概念,而是first come ,first served。如果翻译ؓ先进先服务,或先处理都不是很适合。联惛_本文在介l该概念Ӟ以商物时需要排队ؓ例,索性将其译为“排队顺序”。我惻I有排队意识的人应该能明白其中的含义吧。那么与之对应的Q对于堆栈,只有名ؓ“反排队序”,来代表(First Come, Last ServedQ。希望各位朋友能有更好地译来取代我q个拙劣的词语。ؓ什么不译为“先q先出”,“先q后出”呢Q我主要考虑到这里的英文servedQ它所包含的含义很q,臛_我们可以其认ؓ是对数据的处理,因而就不是单地输出那么单。所以我q脆避开q个词语的含义?BR>
“反排队序”——堆栈数据结?/P>

Queue数据l构通过使用内部存储objectcd的环形数l以实现“排队顺序”的机制。Queue提供了Enqueue()和Dequeue()Ҏ实现数据讉K。“排队顺序”在处理现实问题时经常用刎ͼ其是提供服务的E序Q例如web服务器,打印队列Q以及其他处理多h的程序?/P>

在程序设计中另外一个经怋用的方式是“反排队序Qfirst come,last servedQ”。堆栈就是这样一U数据结构。在.Net Framework基类库中包含了System.Collection.Stackc,和Queue一PStack也是通过存储objectcd数据对象的内部环形数l来实现。Stack通过两种Ҏ讉K数据——Push(item)Q将数据压入堆栈QPop()则是数据弹出堆栈,q返回其倹{?/P>

一个Stack可以通过一个垂直的数据元素集合来Ş象地表示。当元素压入堆栈Ӟ新元素被攑ֈ所有其他元素的端Q弹出时则从堆栈端U除该项。下面两q图演示了堆栈的压栈和出栈过E。首先按照顺序将数据1??压入堆栈Q然后弹出:
 
图五Q向堆栈压入三个元素
 
囑օQ弹出所有元素后的Stack

注意Stackcȝ~省定w?0个元素,而非Queue?2个元素。和Queue和ArrayList一PStack的容量也可以Ҏ构造函数定制。如同ArrayListQStack的容量也是自动成倍增ѝ(回忆一下:Queue可以Ҏ构造函数的可选项讄增长因子。)

注:Stack通常被称为“LIFO先进后出”或“LIFO后进先出”数据结构?BR>堆栈Q计机U学中常见的隐喻
现实生活中有很多同Queue怼的例子:DMVQ译注:不知道其~写Q恕我孤陋寡闻,不知其意Q、打CQ务处理等。然而在现实生活很难扑ֈ和Stackq似的范例,但它在各U应用程序中却是一U非帔R要的数据l构?/P>

设想一下我们用以编E的计算aQ例如:C#。当执行C#E序ӞCLRQ公paq行Ӟ调用Stack以跟t功能模块(译注Q这里原文ؓfunctionQ我理解作者的含义不仅仅代表函敎ͼ事实上很多编译器都会调用堆栈以确定其地址Q的执行情况。每当调用一个功能模块,相关信息׃压入堆栈。调用结束则弹出堆栈。堆栈顶端数据ؓ当前调用功能的信息。(如要查看功能调用堆栈的执行情况,可以在Visual Studio.Net下创Z个项目,讄断点QbreakpointQ,在执行调试。当执行到断ҎQ会在调试窗口(Debug/Windows/Call StackQ下昄堆栈信息?/P>

序数索引的限?/P>

我们在第一部分中讲到数l的特点是同U类型数据的集合Qƈ通过序数q行索引。即Q访问第i个元素的旉为定倹{(误住此U定量时间被标记为O(1)。)

也许我们q没有意识到Q其实我们对有序数据L“情有独钟”。例如员工数据库。每个员工以C保Psocial security numberQؓ其唯一标识。社保号的格式ؓDDD-DD-DDDDQD的范围ؓ数字0—?Q。如果我们有一个随机排列存储所有员工信息的数组Q要查找C保号ؓ111-22-3333的员工,可能会遍历数l的所有元素——即执行O(nQ次操作。更好的办法是根据社保号q行排序Q可其查找旉~减为O(log n)?/P>

理想状态下Q我们更愿意执行O(1)ơ时间就能查扑ֈ某员工的信息。一U方案是建立一个巨型的数组Q以实际的社保号gؓ其入口。这hl的h点ؓ000-00-0000?99-99-9999Q如下图所C:
 
图七Q存储所?位数数字的巨型数l?/P>

如图所C,每个员工的信息都包括姓名、电话、薪水等Qƈ以其C保号ؓ索引。在q种方式下,讉KL一个员工信息的旉均ؓ定倹{这U方案的~点是I间极度的浪费——共?09Q即10亿个不同的社保号。如果公司只?000名员工,那么q个数组只利用了0.0001%的空间。(换个角度来看Q如果你要让q个数组充分利用Q也怽的公怸得不雇䄦全世界h口的六分之一。)

用哈希函数压~序数烦?/P>

显而易见,创徏10亿个元素数组来存?000名员工的信息是无法接受的。然而我们又q切需要提高数据访问速度以达C个常量时间。一U选择是用员工社保号的最后四位来减少C保L跨度。这样一来,数组的跨度只需要从0000?999。图八显CZ压羃后的数组?BR> 
囑օQ压~后的数l?/P>

此方案既保证了访问耗时为常量|又充分利用了存储I间。选择C保L后四位是随机的,我们也可以Q意的使用中间四位Q或者选择W????位?/P>

在数学上这U?位数转换?位数成ؓ哈希转换QhashingQ。哈希{换可以将一个烦引器I间Qindexers spaceQ{换ؓ哈希表(hash tableQ?/P>

哈希函数实现哈希转换。以C保L例子来说Q哈希函数H()表示为:
H(x) = x 的后四位

哈希函数的输入可以是L的九位社保号Q而结果则是社保号的后四位数字。数学术语中Q这U将九位数{换ؓ四位数的ҎUCؓ哈希元素映射Q如图九所C:
 
图九Q哈希函数图C?/P>

图九阐明了在哈希函数中会出现的一U行为——冲H(collisionsQ。即我们一个相对大的集合的元素映射到相对小的集中时Ӟ可能会出现相同的倹{例如社保号中所有后四位?000的均被映ؓ0000。那?00-99-0000Q?13-14-0000Q?33-66-0000Q还有其他的很多都将?000?/P>

看看之前的例子,如果我们要添加一个社保号?23-00-0191的新员工Q会发生什么情况?昄试图d该员工会发生冲突Q因为在0191位置上已l存在一个员工?/P>

数学标注Q哈希函数在数学术语上更多地被描qCؓfQA->B。其中|A|>|B|Q函数f不是一一映射关系Q所以之间会有冲H?/P>

昄冲突的发生会产生一些问题。在下一节,我们会看看哈希函C冲突发生之间的关p,然后单地犯下处理冲突的几U机制。接下来Q我们会注意力攑֜System.Collection.Hashtablec,q提供一个哈希表的实现。我们会了解有关Hashtablecȝ哈希函数Q冲H解xӞ以及一些用Hashtable的例子?/P>

避免和解军_H?/P>

当我们添加数据到哈希表中Q冲H是D整个操作被破坏的一个因素。如果没有冲H,则插入元素操作成功,如果发生了冲H,需要判断发生的原因。由于冲H生提高了代hQ我们的目标是要尽可能冲H压x低?/P>

哈希函数中冲H发生的频率与传递到哈希函数中的数据分布有关。在我们的例子中Q假定社保号是随机分配的Q那么用最后四位数字是一个不错的选择。但如果C保h以员工的出生q䆾或出生地址来分配,因ؓ员工的出生年份和地址昄都不是均匀分配的,那么选用后四位数׃因ؓ大量的重复而导致更大的冲突?/P>

注:对于哈希函数值的分析需要具备一定的l计学知识,q超Z本文讨论的范围。必要地Q我们可以用Kl_k slotsQ的哈希表来保证避免冲突Q它可以一个随机g哈希函数的域中映到L一个特定元素,q定在1/k的范围内。(如果q让你更加的p涂Q千万别担心Q)

我们选择合适的哈希函数的方法成为冲H避免机Ӟcollision avoidanceQ,已有许多研究设计q一领域Q因为哈希函数的选择直接影响了哈希表的整体性能。在下一节,我们会介l在.Net Framework的HashtablecM对哈希函数的使用?/P>

有很多方法处理冲H问题。最直接的方法,我们UCؓ“冲H解x制”(collision resolutionQ,是将要插入到哈希表中的对象放到另外一块空间中Q因为实际的I间已经被占用了。其中一U最单的ҎUCؓ“线性挖掘”(linear probingQ,实现步骤如下Q?BR>1Q?nbsp;当要插入一个新的元素时Q用哈希函数在哈希表中定位;
2Q?nbsp;查表中该位置是否已经存在元素Q如果该位置内容为空Q则插入q返回,否则转向步骤3?BR>3Q?nbsp;如果该地址为iQ则查i+1是否为空Q如果已被占用,则检查i+2Q依此类推,知道扑ֈ一个内容ؓI的位置?/P>

例如Q如果我们要五个员工的信息插入到哈希表中:Alice(333-33-1234)QBob(444-44-1234), Cal (555-55-1237), Danny (000-00-1235), and Edward (111-00-1235)。当d完信息后Q如囑֍所C:
 
囑֍Q有怼C保L五位员工

Alice的社保号被“哈希(q里做动词用Q译注)”ؓ1234Q因此存放位|ؓ1234。接下来来,Bob的社保号也被“哈希”ؓ1234Q但׃位置1234处已l存在Alice的信息,所以Bob的信息就被放C一个位|—?235。之后,dCalQ哈希gؓ1237Q?237位置为空Q所以Cal放?237处。下一个是DannyQ哈希gؓ1235?235已被占用Q则?236位置是否为空。既然ؓI,Dannyp攑ֈ那儿。最后,dEdward的信息。同样他的哈希好?235?235已被占用Q检?236Q也被占用了Q再?237Q直到检查到1238Ӟ该位|ؓI,于是Edward被放C1238位置?/P>

搜烦哈希表时Q冲H仍然存在。例如,如上所C的哈希表,我们要访问Edward的信息。因此我们将Edward的社保号111-00-1235哈希?235Qƈ开始搜索。然而我们在1235位置扑ֈ的是BobQ而非Edward。所以我们再搜烦1236Q找到的却是Danny。我们的U性搜索l查扄道找到Edward或找到内容ؓI的位置。结果我们可能会得出l果是社保号?11-00-1235的员工ƈ不存在?/P>

U性挖掘虽然简单,但ƈ是解军_H的好的{略Q因为它会导致同c聚合(clusteringQ。如果我们要d10个员工,他们的社保号后四位均?344。那么有10个连l空_?344?353均被占用。查找这10个员工中的Q一员工都要搜烦q一位|空间。而且Q添加Q何一个哈希值在3344?353范围内的员工都将增加q一空间的长度。要快速查询,我们应该让数据均匀分布Q而不是集中某几个地方形成一?/P>

更好的挖掘技术是“二ơ挖掘”(quadratic probingQ,每次查位|空间的步长以^方倍增加。也是_如果位置s被占用,则首先检?/SPAN>s+12处,然后?/SPAN>s-12Q?/SPAN>s+22Q?/SPAN>s-22Q?/SPAN>s+32 依此cLQ而不是象U性挖掘那样从s+1Qs+2……线性增ѝ当然二ơ挖掘同样会D同类聚合?/P>

下一节我们将介绍W三U冲H解x制——二度哈希,它被应用?Net Framework的哈希表cM?/P>

System.Collections.Hashtable c?BR>.Net Framework 基类库包括了Hashtablecȝ实现。当我们要添加元素到哈希表中Ӟ我们不仅要提供元素(itemQ,q要元素提供关键字(keyQ。Key和item可以是Q意类型。在员工例子中,key为员工的C保Pitem则通过Add()Ҏ被添加到哈希表中?/P>

要获得哈希表中的元素QitemQ,你可以通过key作ؓ索引讉KQ就象在数组中用序数作ؓ索引那样。下面的C#程序演CZq一概念。它以字W串g为keyd了一些元素到哈希表中。ƈ通过key讉K特定的元素?/P>

using System;
using System.Collections;

public class HashtableDemo
{
   private static Hashtable ages = new Hashtable();

   public static void Main()
   {
        // Add some values to the Hashtable, indexed by a string key
        ages.Add("Scott", 25);
        ages.Add("Sam", 6);
        ages.Add("Jisun", 25);
       
        // Access a particular key
        if (ages.ContainsKey("Scott"))
        {
            int scottsAge = (int) ages["Scott"];
            Console.WriteLine("Scott is " + scottsAge.ToString());
        }
        else
            Console.WriteLine("Scott is not in the hash table...");
   }
}
E序中的ContainsKey()ҎQ是Ҏ特定的key判断是否存在W合条g的元素,q回布尔倹{HashtablecM包含keys属性(propertyQ,q回哈希表中使用的所有关键字的集合。这个属性可以通过遍历讉KQ如下:

// Step through all items in the Hashtable
foreach(string key in ages.Keys)
Console.WriteLine("Value at ages[\"" + key + "\"] = " + ages[key].ToString());

要认识到插入元素的顺序和关键字集合中key的顺序ƈ不一定相同。关键字集合是以存储的关键字对应的元素ؓ基础Q上面的E序的运行结果是Q?/P>

Value at ages["Jisun"] = 25
Value at ages["Scott"] = 25
Value at ages["Sam"] = 6

即插入到哈希表中的序是:ScottQSamQ?Jisun?/P>

Hashtablecȝ哈希函数

HashtablecM的哈希函数比我们前面介绍的社保号的哈希值更加复杂。首先,要记住的是哈希函数返回的值是序数。对于社保号的例子来说很Ҏ办到Q因为社保号本n是数字。我们只需要截取其最后四位数Q就可以得到合适的哈希倹{然而HashtablecM可以接受Mcd的g为key。就象上面的例子Qkey是字W串cdQ如“Scott”或“Sam”。在q样一个例子中Q我们自然想明白哈希函数是怎样string转换为数字的?/P>

q种奇妙的{换应该归功于GetHashCode()ҎQ它定义在System.ObjectcM。ObjectcMGetHashCode()默认的实现是q回一个唯一的整数g保证在object的生命期中不被修攏V既然每U类型都是直接或间接从Objectz的,因此所以object都可以访问该Ҏ。自Ӟ字符串或其他cd都能以唯一的数字值来表示?/P>

HashtablecM的对于哈希函数的定义如下Q?/P>

H(key) = [GetHash(key) + 1 + (((GetHash(key) >> 5) + 1) % (hashsize ?1))] % hashsize

q里的GetHash(key)Q默认ؓ对key调用GetHashCode()Ҏ的返回|虽然在用HashtableӞ你可以自定义GetHash()函数Q。GetHash(key)>>5表示得到key的哈希|向右Ud5位,相当于将哈希值除?2?操作W就是之前介l的求模q算W。Hashsize指的是哈希表的长度。因q行求模Q因此最后的l果HQkQ在0到hashsize-1之间。既然hashsize为哈希表的长度,因此l果L在可以接受的范围内?/P>

HashtablecM的冲H解x?/P>

当我们在哈希表中d或获取一个元素时Q会发生冲突。插入元素时Q必L扑ֆ容ؓI的位置Q而获取元素时Q即使不在预期的位置处,也必L到该元素。前面我们简单地介绍了两U解军_H的机制——线性和二次挖掘。在HashtablecM使用的是一U完全不同的技术,成ؓ二度哈希QrehasingQ?有的资料也将其称为双_ֺ哈希double hashing)?/P>

二度哈希的工作原理如下:有一个包含多个哈希函敎ͼH1……HnQ的集合。当我们要从哈希表中d或获取元素时Q首先用哈希函数H1。如果导致冲H,则尝试用H2Q一直到Hn。各个哈希函数极其相|不同的是它们选用的乘法因子。通常Q哈希函数Hk的定义如下:
Hk(key) = [GetHash(key) + k * (1 + (((GetHash(key) >> 5) + 1) % (hashsize ?1)))] % hashsize

注:q用二度哈希重要的是在执行了hashsizeơ挖掘后Q哈希表中的每一个位|都切地被有且仅有一ơ访问。也是_对于l定的keyQ对哈希表中的同一位置不会同时使用Hi和Hj。在HashtablecM使用二度哈希公式Q其保证为:(1 + (((GetHash(key) >> 5) + 1) % (hashsize ?1))与hashsize两者互为素数。(两数互ؓ素数表示两者没有共同的质因子。)如果hashsize是一个素敎ͼ则保证这两个C为素数?/P>

二度哈希较前两种机制较好地避免了冲突?/P>

调用因子Qload factorsQ和扩充哈希?/P>

HashtablecM包含一个私有成员变量loadFactorQ它指定了哈希表中元素个C表位|L之间的最大比例。例如:loadFactor{于0.5Q则说明哈希表中只有一半的I间存放了元素|其余一半皆为空?/P>

哈希表的构造函C重蝲的方式,允许用户指定loadFactor|定义范围?.1?.0。要注意的是Q不你提供的值是多少Q范围都不超q?2%。即使你传递的gؓ1.0QHashtablecȝloadFactorD?.72。微软认为loadFactor的最佛_gؓ0.72Q因此虽焉认的loadFactor?.0Q但pȝ内部却自动地其改变?.72。所以,你用缺省?.0Q事实上?.72Q有些迷惑,不是吗?Q?/P>

注:我花了好几天旉d询微软的开发h员ؓ什么要使用自动转换Q我弄不明白Qؓ什么他们不直接规定gؓ0.072?.72之间。最后我从编写Hashtablecȝ开发团队的C{案Q他们非常将问题的缘由公怺众。事实上Q这个团队经q测试发现如果loadFactor过?.72Q将会严重的影响哈希表的性能。他们希望开发h员能够更好地使用哈希表,但却可能C?.72q个无规律数Q相反如果规?.0为最佛_|开发者会更容易记住。于是,Ş成现在的l果Q虽然在功能上有许牺牲Q但却我们能更加方便地使用数据l构Q而不用感到头疹{?/P>

向HashtablecL加新元素Ӟ都要q行查以保证元素与空间大的比例不会过最大比例。如果超q了Q哈希表I间被扩充。步骤如下:
1Q?nbsp;哈希表的位置I间q似地成倍增加。准地_位置I间g当前的素数值增加到下一个最大的素数倹{(回想一下前面讲到的二度哈希的工作原理,哈希表的位置I间值必L素数。)
2Q?nbsp;既然二度哈希Ӟ哈希表中的所有元素值将依赖于哈希表的位|空间|所以表中所有g需要二度哈希(因ؓ在第一步中位置I间值增加了Q?/P>

q运的是QHashtablecM的Add()Ҏ隐藏了这些复杂的步骤Q你不需要关心它的实现细节?/P>

调用因子Qload factorQ对冲突的媄响决定于哈希表的M长度和进行挖掘操作的ơ数。Load factor大Q哈希表密集,I间p,比较于相对稀疏的哈希表,q行挖掘操作的次数就多。如果不作精地分析Q当冲突发生时挖掘操作的预期ơ数大约?/(1-lf)Q这里lf指的是load factor?/P>

如前所qͼ微Y哈希表的缺省调用因子设定ؓ0.72。因此对于每ơ冲H,q_挖掘ơ数?.5ơ。既然该数字与哈希表中实际元素个数无养I因此哈希表的渐进讉K旉为OQ?Q,昄q远好于数组的O(n)?/P>

最后,我们要认识到对哈希表的扩充将以性能损耗ؓ代h。因此,你应该预先估计你的哈希表中最后可能会容纳的元素LQ在初始化哈希表时以合适的D行构造,以避免不必要的扩充?BR>



梦在天 2005-12-24 15:38 发表评论
]]>
数据l构?一Q?/title><link>http://www.shnenglu.com/mzty/archive/2005/12/24/2053.html</link><dc:creator>梦在天</dc:creator><author>梦在天</author><pubDate>Sat, 24 Dec 2005 07:36:00 GMT</pubDate><guid>http://www.shnenglu.com/mzty/archive/2005/12/24/2053.html</guid><wfw:comment>http://www.shnenglu.com/mzty/comments/2053.html</wfw:comment><comments>http://www.shnenglu.com/mzty/archive/2005/12/24/2053.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/mzty/comments/commentRss/2053.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/mzty/services/trackbacks/2053.html</trackback:ping><description><![CDATA[<div id="udcftsk" class=postTitle><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><BR> </DIV> <div id="jjisymo" class=postText> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt">W一部分</SPAN><SPAN lang=EN-US>:</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">数据l构?BR></P></SPAN> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p></o:p></SPAN> </P><SPAN lang=EN-US><o:p> <P><SPAN lang=EN-US style="FONT-SIZE: 12pt">原文链接Q?A >Part 1: An Introduction to Data Structures<o:p></o:p></A></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"> </o:p></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT face="Times New Roman">介绍:<BR>本文是介l在.Netq_下用数据结构的pd文章,共分为六部分,q是本文的第一部分.本文试图考察几种数据l构,其中有的包含?Net Framework的基cd?有的是我们自己创建的.如果你对q些名词不太熟悉,那么我们可以把数据结构看作是一U抽象结构或是类,它通常用来l织数据,q提供对数据的操?最常见qؓ我们所熟知的数据结构就是数larray,它包含了一l连l的数据,q过索引q行讉K.</FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT face="Times New Roman">在阅L文内容之?让我们先看看q六部分的主要内?如果你有什么想?或觉得本文有什么遗漏之?希望你通过e-mail(<A href="mailto:mitchell@4guysfromrolla.com">mitchell@4guysfromrolla.com</A>)和我联系,共同分n你的思想.假如有时间的?我很高兴你的徏议放到合适的部分,如有必要,可以在这系列文章中加上W七部分.</FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT face="Times New Roman">W一部分:首先介绍数据l构在算法设计中的重要?军_数据l构的优劣在于其性能.我们经q严格分析数据结构的各种性能.此部分还介l?Net Frameword下两U常用的数据机构:Array 和ArrayList.我们考察其结构的操作方式及其效率.</FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT face="Times New Roman">W二部分:我们l从更多l节上分析ArrayListl构,同时q将介绍QueuecdStackc?和ArrayList一?Queue和Stack存放的都是一l连l的数据集合,都属?Net Framework基类?与ArrayList不同的是,Stack和Queue只能以预先规定的序列序d其数?先进先出和先q后?,而ArrayList可以L获取数据?我们通过CZE序来考察Queue,Stack,q过扩展ArrayListcL实现它们.之后,我们q要分析哈希表HashTable,它象ArrayList一样可以直接访问数?不同的是它以key(字符?为烦?</FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT face="Times New Roman">ArrayListҎ据直接读取和存储是一U理想的数据l构,同时,它也是支持数据搜索的候选方?在第三部?我们考察二叉树结?对于数据搜烦而言,它比ArrayList更加有效. .Net Frameworkq不包含此种内置数据l构,因此需要我们自己创?</FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT face="Times New Roman">二叉树搜索的效率受制于插入到树中的数据的序.如果我们插入的是有序或近似有序的数据,实际?它的效率不如ArrayList.Z这两种的优势结合v?在第四部?我门考察一U有的随机数据l构——SkipList. SkipList既保留了二叉树搜索的高效?同时输入数据的顺序对其效率媄响甚?</FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT face="Times New Roman">W五部分我们注意力转向通常用来表现囑Ş的数据结??graph)是众多节点以及节点之间边的集?举例来说,地图可以图的Ş式来表现.城市是节?公\则是q接节点之间的边.许多现实问题都可以抽象成囄形式,因此,图也是我们经常要用到的数据结?</FONT></SPAN></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT face="Times New Roman">最?W六部分我们谈到reprisent sets(表示?)和disjoint sets(非关联集,即交集ؓI?)集合是一U无序数据的集中.非关联集是指它和另外一个集合没有共同的元素.我们在程序编写时会经常用到集合和非关联集.我们在q一部分中详l描q它.</FONT></SPAN></P><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT face="Times New Roman"> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><BR>数据l构性能分析</P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt">当我们在思考一个特别的应用E序或者程序的问题?多数开发h?包括我自?都将兴趣集中到算法上以解x头的N,或者ؓ应用E序加上一个很L特色以丰富用Ll验.我们g很少听到有h会ؓ他所使用的数据结构而激动不?啧啧赞叹. 然?用在一个特定算法中的数据结构能够很大程度上影响其性能.最常见的例子就是在数据l构中查找一个元?在数l中,查找q程所耗时间是与这个数l中元素的个数是成正比的.采用二叉数或者SkipLists(我找不到合适的译,按前所q?它包含了随机数的集合,也许看了后面的部分会惛_合适的中文),耗时与数据个数比例成U型下降(sub-linear,我又黔驴词穷?.当我们要搜烦大量的数据时,数据l构的选择对程序的性能其重要,其差别甚臌到数U?乃至于数分钟.</P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt">既然在算法中使用的数据结构媄响了法的效?因此比较各种数据l构的效率ƈ从中选择一U更佳的Ҏ显得尤为重?作ؓ开发者而言,我们首先要关注的是随着存储的数据量的增?数据l构性能是怎样随之改变的的?也就是说,每当数据l构中添加一个新元素?它将怎样影响数据l构的运行时?</P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt">考虑q样一U情?我们在程序中使用了System.IO.Directory.GetFiles(路径)Ҏ以返回文件的列表,存放C个特定的字符串数ldirectory?假设你需要搜索这个数l以判断在文件列表中是否存在XML文g(x展名?xml的文?,一U方法是扫描(scan,或者是遍历)整个数组,当找到XML文g?p|一个标?代码可能是这?</P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt">using System;<BR>using System.Collections;<BR>using System.IO;</P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt">public class MyClass<BR>{<BR>   public static void Main()<BR>   {<BR>      string [] fs = Directory.GetFiles(@"C:\Inetpub\wwwroot");<BR>      bool foundXML = false;<BR>      int i = 0;<BR>      for (i = 0; i < fs.Length; i++)<BR>         if (String.Compare(Path.GetExtension(fs[i]), ".xml", true) == 0)<BR>         {<BR>            foundXML = true;<BR>            break;<BR>         }<BR>   <BR>     if (foundXML)<BR>        Console.WriteLine("XML file found - " + fs[i]);<BR>     else<BR>        Console.WriteLine("No XML files found.");<BR>      <BR>   }<BR>}</P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><BR>现在我们来看看最p糕的一U情?当这个列表中不存在XML文g或者XML文g是在列表的最?我们会搜烦完这个数l的所有元?再来分析一下数l的效率,我们必须问问自己,"假设数组中现有n个元?如果我添加一个新元素,增长为n+1个元?那么新的q行旉是多?(术语"q行旉"--running time,不能֐思义地认为是E序q行所消耗的l对旉,而指的是E序完成该Q务所必须执行的步骤数.以数l而言,q行旉特定被认为是讉K数组元素所需执行的步骤数?要搜索数l中的一个|潜在的可能是讉K数组的每一个元素,如果数组中有n+1个元素,将执行n+1ơ检查。那是_搜烦数组耗费的时间与数组元素个数成几何线形比?/P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt">当数据结构的长度于无穷大时Q分析其l构的效率,我们把这U分析方法称为渐q分析(asymptotic analysis)。渐q分析中常用的符h大写的OQbig-Oh)Q以O(n)的Ş式描q遍历数l的性能。O是术语学中big-OhW号的表C,n则代表遍历数l时随长度增长而与之线形增长的E序执行步数?/P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt">计算代码块中法的运行时间的一U系l方法应遵@以下步骤Q?/P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt">1、判断组成算法运行时间的步骤。如前所qͼ对于数组而言Q典型的步骤应是Ҏl进行读写访问的操作。而对于其他数据结构则不尽然。特别地Q你应该考虑的是数据l构自n的步骤,而与计算机内部的操作无关。以上面的代码块ZQ运行时间应该只计算讉K数组的次敎ͼ而不用考虑创徏和初始化变量以及比较两个字符串是否相{的旉?BR>2、找到符合计运行时间条件的代码行。在q些行上面置1?BR>3、判断这些置1的行是否包含在@环中Q如果是Q则?改ؓ1乘上循环执行的最大次数。如果嵌套两重或多重循环Ql对循环做相同的乘法?BR>4、找到对每行写下的最大|它就是运行时间?/P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt">现在我们按照q种步骤来标C面的代码块。首先我们已l能够确定与计算q行旉有关的代码行Q再Ҏ步骤2Q在数组fs被访问的两行代码作上标记Q一行是数组元素作ؓString.Compare()Ҏ的参敎ͼ一行是在Console.WriteLine()Ҏ中。我们将q两行标Cؓ1。然后根据步?QString.Compare()Ҏ是在循环中,最大@环次CؓnQ因为数l长度ؓnQ。因此将该行的标?改ؓn。最后,我们得到的运行时间就是标记的最大值nQ记为O(n)。(译注Q即为数据结构中通常所说的旉复杂度)</P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt">O(n)Q或者说UŞ旉(linear-time)Q表CZ多种法q行旉中的一U。其他还有O(log2 n)QO(n log 2 n)QO(n2)QO(2n){等。我们无d心这些繁杂的big-Oh记号Q只需要知道在括号中的D,则代表数据结构的性能好。D例来_旉复杂度(在这里我q是觉得用时间复杂度比运行时间更能理解)为O(log n)的算法远比O(n)更有效率Q因为log n<N?BR></P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><BR>注:</P> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">我们需要温习以下数学知识。在q里Q?/SPAN><SPAN lang=EN-US>log <SUB>a</SUB> b</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">另外一U表C方法ؓ</SPAN><SPAN lang=EN-US>a<SUP>y</SUP>=b</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。因此,</SPAN><SPAN lang=EN-US>log<SUB>2</SUB>4=2</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">Q因?/SPAN><SPAN lang=EN-US>2<SUP>2</SUP>=4</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">?/SPAN><SPAN lang=EN-US>Log<SUB>2</SUB>n</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">增长速度比单个的</SPAN><SPAN lang=EN-US>n</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">要慢得多Q在W三部分我们考察旉复杂度ؓ</SPAN><SPAN lang=EN-US>O(log<SUB>2</SUB>n)</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的二叉树l构。(q个注释没多大意思啊Q)</SPAN></P></FONT></SPAN> <P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"></SPAN></P> <P>在这系列文章中Q我们将计算每一U新的数据结构和它们的渐q操作运行时_q过怼的操作比较其他数据结构在q行旉上的区别?/P> <P>数组Q一U线形的Q可以直接访问的Q单一数据l构</P> <P>在程序编写中Q数l是最单也是最q泛使用的数据结构。在所有的E序语言中数l都具备以下共同的属性:<BR>1Q数l的数据存储在一D连l的内存之中Q?BR>2Q数l的所有元素都必须是同一U数据类型,因此数组又被认ؓ是单一数据l构(homogeneous data structures)Q?BR>3Q数l元素可以直接访问。(在很多数据结构中Q这一特点是不必要的。例如,文章W四部分介绍的数据结构SkipList。要讉KSkipList中的特定元素Q你必须Ҏ搜烦其他元素直到扑ֈ搜烦对象为止。然而对于数l而言Q如果你知道你要查找Wi个元素,可以通过arrayName[i]来访问它。)Q译注:很多语言都规定数l的下标?开始,因此讉KWi个元素,应ؓarrayName[i-1]Q?/P> <P>以下是数l常用的操作Q?BR>1Q分配空?BR>2Q数据访?BR>3Q数l空间重分配QRedimensioningQ?/P> <P>在C#里声明数l时Q数lؓI|nullQ。下面的代码创徏了一个名为booleanArray的数l变量,其gؓI(nullQ:</P> <P>Bool [] boolleanArray;</P> <P>在用该数组Ӟ必须用一个特定数字给它分配空_如下所C:</P> <P>booleanArray = new bool[10];</P> <P>通用的表qCؓQ?/P> <P>arrayName = new arrayType[allocationSize];</P> <P>它将在CLR托管堆里分配一块连l的内存I间Q以容Ux据类型ؓarrayTypes、个CؓallocationSize的数l元素。如果arrayType为值类型(译注Q如intcdQ,则有allocationSize个未箱QunboxedQ的arrayTypeD创徏。如果arrayType为引用类?译注Q如stringcd)Q则有allocationSize个arrayType引用cdD创徏。(如果你对值类型和引用cd、托堆和栈之间的区别不熟悉Q请查阅“理?Net公共cdpȝCommon Type System”)</P> <P>为帮助理?Net Framework中数l的内部存储机制Q请看下面的例子Q?/P> <P>arrayName = new arrayType[allocationSize];</P> <P>This allocates a contiguous block of memory in the CLR-managed heap large enough to hold the allocationSize number of arrayTypes. If arrayType is a value type, then allocationSize number of unboxed arrayType values are created. If arrayType is a reference type, then allocationSize number of arrayType references are created. (If you are unfamiliar with the difference between reference and value types and the managed heap versus the stack, check out Understanding .NET's Common Type System.)</P> <P>To help hammer home how the .NET Framework stores the internals of an array, consider the following example:</P> <P>bool [] booleanArray;<BR>FileInfo [] files;</P> <P>booleanArray = new bool[10];<BR>files = new FileInfo[10];</P> <P>q里QbooleanArray是值类型System.Boolean数组Q而files数组则是引用cdSystem.IO.FileInfo数组。图一昄了执行这四行代码后CLR托管堆的情况?BR></P> <P><SPAN lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-fareast-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /><v:shapetype id=_x0000_t75 coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"> <IMG height=220 src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/1-1.gif" width=450 border=0><v:stroke joinstyle="miter"></v:stroke><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f></v:formulas><v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></v:path><o:lock v:ext="edit" aspectratio="t"></o:lock></v:shapetype></SPAN><BR> <BR>图一Q在托管堆中序存放数组元素</P> <P>误住在files数组中存攄十个元素指向的是FileInfo实例。图二强调了q一点(hammers home this pointQ有些俚语的感觉Q不知道怎么译Q,昄了如果我们ؓfiles数组中的FileInfo实例分配一些值后内存的分布情c?BR> <BR></P> <P><IMG height=188 src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/1-2.gif" width=450 border=0><BR>图二Q在托管堆中序存放数组元素</P> <P><BR>.Net中所有数l都支持对元素的d操作。访问数l元素的语法格式如下Q?/P> <P>// M个数l元?BR>bool b = booleanArray[7];</P> <P>// 写一个数l元素,卌?BR>booleanArray[0] = false;</P> <P>讉K一个数l元素的q行旉表示为O(1)Q因为对它的讉K旉是不变的。那是_不管数组存储了多元素,查找一个元素所q旉都是相同的。运行时间之所以不变,是因为数l元素是q箋存放的,查找定位的时候只需要知道数l在内存中的起始位置Q每个元素的大小Q以及元素的索引倹{?/P> <P>在托代码中Q数l的查找比实际的实现E微复杂一些,因ؓ在CLR中访问每个数l,都要保索引值在其边界之内。如果数l烦引超界,会抛出IndexOutOfRangeException异常。这U边界检查有助于保我们在访问数l不至于意外地超出数l边界而进入另外一块内存区。而且它不会媄响数l访问的旉Q因为执行边界检查所需旉q不随数l元素的增加而增加?/P> <P>注:如果数组元素特别多,索引边界查会对应用程序的执行性能有稍许媄响。而对于非托管代码Q这U边界检查就被忽略了。要了解更多信息Q请参考Jeffrey Richter所著的Applied Microsoft .NET Framework ProgrammingW?4章?/P> <P>使用数组Ӟ你也讔R要改变数l大。可以通过Ҏ特定的长度大创Z个新数组实例Qƈ旧数组的内Ҏ贝到新数l,来实现该操作。我们称q一q程为数l空间重分配(redimensioning)Q如下代码:</P> <P>using System;<BR>using System.Collections;</P> <P>public class MyClass<BR>{<BR>   public static void Main()<BR>   {<BR>      // 创徏包含3个元素的intcd数组<BR>      int [] fib = new int[3];<BR>      fib[0] = 1;<BR>      fib[1] = 1;<BR>      fib[2] = 2;<BR>      <BR>      // 重新分配数组Q长度ؓ10<BR>      int [] temp = new int[10];</P> <P>// fib数组内容拯C时数l?BR>      fib.CopyTo(temp, 0);<BR>      <BR>      // 时数l赋lfib<BR>      fib = temp;   <BR>   }<BR>}</P> <P>在代码的最后一行,fib指向包含10个元素的Int32cd数组。Fib数组??Q译注:注意下标?开始)的元素值默认ؓ0QInt32cdQ?/P> <P>当我们要存储同种cd的数据(原文为heterogeneous types——异cL据类型,我怀疑有误)q仅需要直接访问数据时Q数l是较好的数据结构。搜索未排序的数l时间复杂度是线形的。当我们对小型数l进行操作,或很对它进行查询操作时Q数l这U结构是可以接受的。但当你的应用程序需要存储大量数据,且频J进行查询操作时Q有很多其他数据l构更能适应你的工作。我们来看看本文接下来将要介l的一些数据结构。(如果你要Ҏ某个属性查找数l,且数l是Ҏ该属性进行排序的Q你可以使用二叉法(binary searchQ对其搜索,它的旉复杂度ؓO(log n)Q与在二叉树中搜索的旉复杂度相同。事实上Q数l类中包含了一个静态方法BinarySearch()。如要了解该Ҏ的更多信息,请参考我早期的一文章“有效地搜烦有序数组”?/P> <P>注:.Net Framework同样支持多维数组。与一l数l一P多维数组Ҏ据元素的讉Kq行旉仍然是不变的。回想一下我们前面介l的在n个元素的一l数l中查询操作的时间复杂度为O(n)。对于一个nxn的二l数l,旉复杂度ؓO(n2)Q因为每ơ搜索都要检查n2个元素。以此类推,kl数l搜索的旉复杂度ؓOQnkQ?/P> <P>ArrayListQ可存储不同cd数据、自增长的数l?/P> <P>明确圎ͼ数组在设计时受到一些限Ӟ因ؓ一l数l只能存储相同类型的数据Q而且在用数l时Q必Mؓ数组定义特定的长度。很多时候,开发h员要求数l更加灵z,它可以存储不同类型的数据Q也不用d心数l空间的分配。在.Net Framework基类库中提供了满h件的数据l构——System.Collections.ArrayList?/P> <P>如下的一段代码是ArrayList的示例。注意到使用ArrayList时可以添加Q意类型的数据Q且不需要分配空间。所有的q些都由pȝ控制?/P> <P>ArrayList countDown = new ArrayList();<BR>countDown.Add(5);<BR>countDown.Add(4);<BR>countDown.Add(3);<BR>countDown.Add(2);<BR>countDown.Add(1);<BR>countDown.Add("blast off!");<BR>countDown.Add(new ArrayList());</P> <P>从深层次的含义来ԌArrayList使用的存攄型ؓobject的System.Array对象。既然所有类型都是直接或间接从objectzQ自然一个objectcd的数l也可以存放Mcd的元素。ArrayList默认创徏16个objectcd元素的数l,当然我们也可以通过构造函C的参数或讄Capacity属性来定制ArrayList大小。通过Add()Ҏd新元素,数组内部自动查其定w。如果添加新元素D界Q则定w则自动成倍增加,我们UCؓ自增ѝ?/P> <P>ArrayList和Array一P也可以通过索引直接讉KQ?/P> <P>// Read access<BR>int x = (int) countDown[0];<BR>string y = (string) countDown[5];</P> <P>// Write access<BR>countDown[1] = 5;</P> <P>// 会生ArgumentOutOfRange 异常<BR>countDown[7] = 5;</P> <P>既然ArrayList存储的是objectcd的元素,因此从ArrayList中读元素时应该显C的指定cd转换。同时要注意的是Q如果你讉K的数l元素超qArrayList的长度,pȝ会抛出System.ArgumentOutOfRange异常?/P> <P>ArrayList提供了标准数l所不具备的自增长灵zL,但这U灵zL是以牺牲性能ZLQ尤其是当我们存储的是值类型——例如System.Int32QSystem.DoubleQSystem.Boolean{。它们在托管堆中是以未封Ş?unboxed form)q箋存放的。然而,ArrayList的内部机制是一个引用的object对象数组Q因此,即ArrayList中只存放了值类型,q些元素仍然会通过箱QboxingQ{换ؓ引用cd。如图三所C:<BR> <IMG height=249 alt=1-3.gif src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/1-3.gif" width=450 border=0></P> <P>图三Q存储连l块的object引用的ArrayList</P> <P>在ArrayList中用值类型,额外进行封?boxing)和撤?unboxing)操作Q当你的应用E序是一个很大的ArrayListQƈ频繁q行d操作Ӟ会很大程度上影响E序性能。如?所C,对于引用cd而言QArrayList和数l的内存分配是相同的?/P> <P>比较数组而言QArrayList的自增长q不会导致Q何性能的下降。如果你知道存储到ArrayList的元素的准确数量Q可以通过ArrayList构造函数初始化定w以关闭其自增长功能。而对于数l,当你不知道具体容量时Q不得不在插入的数据元素过数组长度的时候,手动改变数组的大?/P> <P>一个经典的计算机科学问题是Q当E序q行时超Z~存I间Q应该分配多新的空间ؓ最佟뀂一U方案是是原来分配空间的基础上每ơ加1。例如数l最初分配了5个元素,那么在插入第6个元素之前,其长度增加?。显Ӟq种Ҏ最大程度上节约了内存空_但代价太大,因ؓ每插入一个新元素都要q行一ơ再分配操作?/P> <P>另一U方案刚好相反,也就是每ơ分配都在原来大的基础上增?00倍。如果数l最初分配了5个元素,那么在插入第6个元素之前,数组I间增长?00。显Ӟ该方案大大地减少了再分配操作的次敎ͼ但仅当插入极的数据元素Ӟ׃有上癄元素I间未用,实在太浪费空间了Q?/P> <P>ArrayList的渐q运行时间和标准数组一栗即使对ArrayList的操作是高开销的,其是存储值类型,其元素个数和每次操作的代价之间的关系与标准数l相同?BR></P></DIV><img src ="http://www.shnenglu.com/mzty/aggbug/2053.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/mzty/" target="_blank">梦在天</a> 2005-12-24 15:36 <a href="http://www.shnenglu.com/mzty/archive/2005/12/24/2053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>cQ+单向链表 Q讨论应不应该在默认的构造里分配空_http://www.shnenglu.com/mzty/archive/2005/10/28/870.html梦在天梦在天Fri, 28 Oct 2005 00:42:00 GMThttp://www.shnenglu.com/mzty/archive/2005/10/28/870.htmlhttp://www.shnenglu.com/mzty/comments/870.htmlhttp://www.shnenglu.com/mzty/archive/2005/10/28/870.html#Feedback6http://www.shnenglu.com/mzty/comments/commentRss/870.htmlhttp://www.shnenglu.com/mzty/services/trackbacks/870.html阅读全文

梦在天 2005-10-28 08:42 发表评论
]]>
޾ƷþרӰҵ| 8090Ʒþһ| Ʒþþþþþù˽| ƷۺϾþþþþ98| þˬˬƬAV | þþƷרѶ | þ99žŹѿС˵| һɫþۺϺݺ| ˾þ111վ| þþƷ99Ʒ| Ʒ˾Ʒþþ | AV12þ| 99þ99þþƷ| Ʒþþþþù| þerƷѹۿ2| ˼˼þþƷ| ƷžžþƵ| þþþAV鶹| 8090Ʒþһ| þþƷ7777| þ¾Ʒ| Ʒʾþþþ999Ұ| þþƷަvDz| þݺҹҹav˳| ԭۺϾþô˾Ʒ| þþþAVվ| þþ| ŷպƷþþþ| ݲݾþþר| þùɫAVѿ| ɫAVԾþþþþ| þҹɫ˾Ʒ| ձƷþ| ѾþþƷ99þ| 99þ99þþƷƬ | Ʒþþþþ | þþƷ| þþƷ޾Ʒ| þøһëƬ| ٸִִˬëƬþú| þAVij|