【问题标题】:Why are most of the examples using ArrayList为什么大多数示例都使用 ArrayList
【发布时间】:2011-04-28 01:56:28
【问题描述】:

开发Java,你一直都知道最好使用List接口作为存储列表的变量的类型来创建一个ArrayList。像这样

List<String> myList = new ArrayList<String>();

但是,通过查看捆绑包中包含的许多 android 示例,他们使用 Class 创建了列表。

ArrayList<String> myList = new ArrayList<String>();

这样做有什么理由吗?显式设置类是更快、更轻还是什么?

【问题讨论】:

  • "你一直都知道最好使用 List 接口创建一个 ArrayList"。有趣的是,我以前从未听说过。这样做有什么好处?
  • @Brian 如果您尽可能多地使用接口,代码将与列表的实现分离。更容易将列表替换为仅实现列表接口的另一个对象。像一些奇怪的数据库抽象或其他东西。
  • 示例何时总是使用最佳实践?大多数 MySQL/PHP 示例都充斥着 SQL 注入攻击和其他问题。 Web 上的大多数示例都是以这样的方式完成的,即您可以从尽可能少的代码中获得尽可能多的功能,或者使用尽可能易于理解的代码,同时完全忽略最佳实践、潜在的安全漏洞和错误检查.
  • 回应人们问为什么首先在声明中使用接口:Effective Java 2nd Edition,第 52 条:通过接口引用对象“如果存在适当的接口类型,则参数,返回值和字段都应该使用接口类型声明。如果你养成使用接口类型的习惯,你的程序会灵活很多。如果没有合适的接口,完全可以用类来引用对象。 "

标签: java android arraylist


【解决方案1】:

我建议阅读Performance Myths,它解释了将变量定义为 List 或 ArrayList 的优点和问题。

【讨论】:

  • 很好的链接!他们表明,对于没有 JIT 的设备,直接使用 HashMap 比使用 Map(即 HashMap)快 6%
  • 而且,文档中曾经有一个页面明确指出,对于 Android,您应该避免使用接口类型(因为当然,在 JIT 之前,它比较慢)。无论如何,这就是为什么你会看到这么多这样的 Android 示例,直到最近才被文档提倡。
【解决方案2】:

就我个人而言,我认为“你一直都在学习”这句话没有抓住重点。这个:

List<String> myList = new ArrayList<String>();

几乎没有维护收益。如果你想改变它为不同的实现,如果你使用实现类型,它仍然只需要改变一行。然而,一个完全不同的事情是:

public void process(List<String> list) {

在这里,使用接口真的很重要,因为它允许调用该方法的人使用不同的实现不必更改方法签名(他们可能无法做)。

【讨论】:

  • +1 是时候结束这种伪装了。我附上了我的解释。
  • List myList = new ArrayList() 记录了 myList 的意图,因为代码希望将 myList 用作 List 而不是专门的 ArrayList
  • @steve-kuo 除了代码专门使用 ArrayList,大概是有充分理由的。即使在极少数情况下任何 List 都能很好地工作,我也很难相信程序员会发现有必要明确而有意识地指出这一点。
  • 我不认为它错过了重点,无论如何也不完全是。只是您可以将该接口类型用作参数(如您所记),或返回它,等等,在这些情况下,从灵活性和维护的角度来看,您最好使用该接口。现在,如果您只是在内部使用它,那么是的,但仅使用界面仍然不会伤害您(这是一种习惯)。
  • 我不同意 List myList = new ArrayList();提供很少的维护好处。 If ArrayList myList = new ArrayList();然后使用方法的编码器(或后续编码器)可能会使用特定于 ArrayList 的方法,这可能是一个潜在的维护难题。不过,我同意它作为一个论点更重要。
【解决方案3】:

在像 Android 设计的手机这样的资源受限环境中,最好避免使用接口,因为它涉及额外的虚拟函数调用。

【讨论】:

  • 这是我一直听到的解释,不过,我很想看看现实世界性能方面的差异(从用户的角度来看)。
  • 我同意。需要大量的构造函数调用才能有所作为。我很想看到一个实际的应用程序可以发出足够多的调用以获得性能优势。但话又说回来,我从来没有为 android 编程过......
  • @Daniel Standage:我不认为使用显式类型实际上会影响构造函数调用;每次调用成员函数都要支付“接口税”(虚拟函数调用)。构造函数本身几乎没有受到影响。但是,我同意这需要实际的基准测试来确定这是否仍然重要,因为最新的智能手机配备了相当强大的处理器和相当大的内存。
  • 这是 google 文档中关于节省资源的官方原因。我也将其称为过早优化。尽量使用列表界面。
  • 感谢您的澄清。
【解决方案4】:

虽然可能会带来性能优势,但几乎可以肯定它很小,并且是过早优化的一个例子。

更有可能的解释是示例的作者想让你专注于他(或她)试图教给你的东西。保持变量和它引用的对象相同类型是一件少考虑的事情。已发布的代码示例是一种代码,当您非常确定永远不必修改它时,使用 ArrayList 作为变量类型是完全可以的。

【讨论】:

    【解决方案5】:

    在编译对 List 对象进行操作的代码时,编译器必须使用接口调用,这比对具体类型的常规虚拟调用更昂贵。

    当然,在一段代码中,编译器看到正在实例化的对象并可以证明声明为 List 的东西始终是 ArrayList,编译器应该能够只发出一个正则调用而不是接口调用,但未内联且对已实例化的对象进行操作的方法不会从这种优化中受益。

    普遍使用的加速虚拟调用的优化,即内联缓存(通常是多态内联缓存或 PIC,不要与位置无关代码混淆)得益于观察到只有一个子类的实例通过某种声明类型的变量。在这种情况下,在代码运行一段时间后,JIT 可以乐观地猜测 List 对象将永远是 ArrayList,生成陷阱以防猜测错误,然后使用 ArrayList 打电话。

    现代处理器执行检查的速度非常快(因为它们是超标量并且具有良好的分支预测),因此您不会注意到所有这些虚拟调用和单个实现接口调用的成本。但它确实使 VM 工作更加努力,检测、生成和修补所有代码。

    对于在 HotSpot 上以稳定状态运行的服务器软件,这无关紧要,但对于在移动设备上快速启动,它可能会有所不同 - 我不知道 Google 的 VM 有多好。

    Cliff Click, Jr. 博士撰写的一篇不错的文章(自定义大铁硬件,热点衍生 VM): http://www.azulsystems.com/blog/cliff-click/2010-04-08-inline-caches-and-call-site-optimization

    当然还有维基百科上的“内联缓存”。

    【讨论】:

      【解决方案6】:

      Interface x = new Implementation() 模式是一种流行的抽象抽象尝试。

      变量声明和初始化语句Type x = new Constructor(); 绝对是实现细节的一部分。它不是公共 API 的一部分(除非它是 public final,但 List 是可变的,所以这是不合适的)

      作为一个实现细节,我们试图用这个“抽象”来愚弄谁?最好保持类型尽可能具体。是数组列表还是链表?它是否应该是线程安全的?选择对实现很重要,我们仔细选择了具体的列表impl。然后我们将它声明为一个单纯的 List 就好像它无关紧要,我们不在乎一样?

      将其声明为 List 的唯一正当理由是我懒得打字。这也涵盖了以下论点:如果我需要移动到另一个 List impl,我就少了一个可以修改的地方

      这个理由只有在变量作用域很小的情况下才是合理的,我们一眼就可以看到它的所有用法。否则,声明最具体的类型,以便其性能和语义特征在使用该变量的所有代码中体现出来。

      【讨论】:

        【解决方案7】:

        实际上并没有太大区别,只是在 Android 示例中它们使用了更明确的类型。也许有一些 Android 方法依赖于接收一个 ArrayList 而不仅仅是一个 List。

        【讨论】:

          【解决方案8】:

          这是一个众所周知的“设计原则”,关于如何做出好的设计,“编程到接口,而不是实现”。就像 Michael Borgwardt 说的那样,也许它并不关心将这个原则与你的局部变量一起使用,但是如果你在类型之间有关联,那么编程到接口而不是实现来使用的好处是有意义的 哎呀。实现接口而不是 impl 允许在运行时进行动态多态分配。说,

          interface IAa { say(); }
          class A implements IAa { public void say() { .. says A ... } } 
          class B implements IAa { public void say() { .. says B ... } } 
          
          
          class App {
          
          public void someFoo() {
              IAa myA = new A();
              myA.say(); // says A
              myA = new B();  
              myA.say(); // says B
          
          }
          ...
          }
          

          我不这么认为,Android 编程会有所不同 :)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-06-04
            • 1970-01-01
            • 1970-01-01
            • 2021-10-11
            • 2011-01-18
            • 2019-11-23
            • 1970-01-01
            • 2012-08-20
            相关资源
            最近更新 更多