2010-10-20 63 views
6

开发Java,你一直获悉,其最好使用List接口作为列表存储在该变量的类型来创建一个ArrayList。像这样为什么大多数例子中使用的ArrayList

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

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

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

是否有任何理由这样做?它是更快,更轻或什么明确设置类?

+1

“你总是知道最好通过使用List接口来创建一个ArrayList”。有趣的是,我从来没有听说过。这样做的好处是什么? – 2010-10-20 14:15:55

+5

@Brian如果您尽可能使用接口,代码将从列表的实现中分离出来。更容易将列表替换为仅实现列表接口的另一个对象。像一些奇怪的数据库抽象或其他东西。 – Janusz 2010-10-20 14:20:48

+0

什么时候有例子总是使用最佳实践?大多数MySQL/PHP示例都充斥着SQL注入攻击和其他问题。网络上的大多数示例都是以尽可能少的代码获取尽可能多的功能,或尽可能容易理解的代码,同时完全忽略最佳实践,潜在的安全漏洞和错误检查。 – Kibbee 2010-10-20 15:18:27

回答

9

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

+1

这是我一直听到的解释,但是,我有兴趣看到现实世界的性能差异(从用户的角度来看)。 – 2010-10-20 14:10:44

+2

我同意。这需要大量的构造函数才能有所作为。我会有兴趣看到一个实际的应用程序,足以呼吁那里有一个性能优势。但后来我再也没有编程为Android ... – 2010-10-20 14:17:16

+2

@Daniel Standage:我不认为使用显式类型实际上会影响构造函数调用;每次对成员函数的调用都支付“接口税”(虚函数调用)。构造函数本身几乎没有受到影响。不过,我同意,由于最新的智能手机拥有相当强大的处理器以及大量内存,因此需要真正的基准测试才能看出这一点是否真的很重要。 – 2010-10-20 14:23:58

0

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

2

尽管可能会有性能优势,但它几乎可以确保很小,并且是过早优化的一个例子。

一个更可能的解释是,这些例子的作者想让你专注于他(或她)试图教你的东西。保持变量和它引用相同类型的对象是少一件需要考虑的事情。发布的代码示例是一种代码,如果您确信不需要修改它,那么使用ArrayList作为变量类型是完全正确的。

12

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

+0

伟大的链接!他们发现,对于没有JIT的设备,直接使用HashMap而不是Map(HashMap)的速度提高了6%。 – 2010-10-20 14:51:53

+0

而且,以前在文档中有一个页面,明确指出Android应该避免使用接口类型(因为当然,在JIT之前,它比较慢)。无论如何,这就是为什么你会看到如此之多的Android例子,直到最近它才被文档提倡。 – 2010-10-20 22:27:28

10

就我个人而言,我相信“你一直都在学”的东西没有理解。此:

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

几乎没有维护好处。如果您想将其更改为不同的实现,那么如果使用实现类型,它仍然只有一条线要更改。然而,完全不同的问题是这样的:

​​

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

+0

我完全同意+1 – 2010-10-20 14:48:07

+0

+1现在是结束这个假装的时候了。我附上了我的解释。 – irreputable 2010-10-20 15:43:27

+0

'List myList = new ArrayList()'记录了myList的意图,因为代码希望将myList用作List,而不是特定的ArrayList。 – 2010-10-20 16:13:03

1

编译运行于List对象上的代码时,编译器必须使用接口调用,它比具体类型上的常规虚拟调用更加昂贵。当然,在编译器看到被实例化的对象并能证明被声明为列表的东西总是一个ArrayList <>的代码片段中,编译器应该能够仅仅发出一个定期调用而不是接口调用,但未内联并在已实例化对象上运行的方法不会从此优化中受益。

无处不在的用于加速虚拟调用的优化,即内联缓存(通常为多态内联缓存或PIC,不要与位置独立代码混淆)可从以下观察中受益:只有单个子类的实例可以通过某种声明类型的变量。在这种情况下,代码已经运行仪器了之后,而JIT可以乐观地猜测,一个List <>对象将永远只能是一个ArrayList <>,生成陷阱的情况下,猜测是错误的,并与该ArrayList告吹拨打电话。

现代处理器上执行的检查非常快(因为它们是超标量并具有良好的分支预测),所以你没有注意到所有这些虚拟呼叫和单一实现接口的通话费用。但它确实可以让虚拟机工作得更好,检测,生成和修补所有代码。

有关热点稳态运行的服务器软件,它是无关紧要但对于在移动设备上快速启动可能有所作为的 - 我不知道有多好,谷歌的VM。

它一个很好的文章博士悬崖点击,小(大定制硬件铁,热点-deived VM): http://www.azulsystems.com/blog/cliff-click/2010-04-08-inline-caches-and-call-site-optimization

当然,“内联缓存”在维基百科上。

0

这是一个众所周知的“设计原理”关于HOWTO做出好的设计,“程序的接口,而不是实现”。像迈克尔博格瓦特说,也许它并不关心使用这个原理与局部变量,但如果你有类型之间的关联,那么它是有意义的编程接口而不是实现使用 OOP的好处。实现接口而不是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的程序是不同的:)

1

Interface x = new Implementation()模式是在是抽象的热门天真的尝试。

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

作为一个实现细节,我们试图用这个“抽象”来欺骗?尽可能保持特定类型的更好。它是一个数组列表还是一个链表?它应该是线程安全还是不安全?选择对于实现很重要,我们仔细选择了具体的列表impl。然后,我们将它声明为仅仅列表,就好像它不重要,我们不在乎?

将其声明为List的唯一正当理由是我懒得输入。这也涵盖了如果我需要移动到另一个列表impl我有一个更少的地方来修改的论点。

只有当变量范围很小时,这个原因才是合法的,我们可以一目了然地看到它的所有用法。否则,请声明最具体的类型,以便在使用该变量的所有代码中显示其性能和语义特征。

相关问题