Mat*_*att 8 java generics collections interface
我被告知对局部变量的接口进行编程是没用的,不应该这样做,因为它只会伤害性能并且没有任何好处.
public void foo() {
ArrayList<Integer> numbers = new ArrayList<Integer>();
// do list-y stuff with numbers
}
Run Code Online (Sandbox Code Playgroud)
代替
public void foo() {
List<Integer> numbers = new ArrayList<Integer>();
// do list-y stuff with numbers
}
Run Code Online (Sandbox Code Playgroud)
我觉得性能命中率可以忽略不计,但可以肯定的是,使用ArrayList的List语义并没有太大的收获.是否有充分理由采取这种或那种方式?
Tim*_*ote 14
以这堂课为例:
public class Tmp {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(1);
}
}
Run Code Online (Sandbox Code Playgroud)
它归结为:
Compiled from "Tmp.java"
public class Tmp extends java.lang.Object{
public Tmp();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: iconst_1
10: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokevirtual #5; //Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
16: pop
17: return
}
Run Code Online (Sandbox Code Playgroud)
虽然这堂课:
public class Tmp {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<Integer>();
numbers.add(1);
}
}
Run Code Online (Sandbox Code Playgroud)
编译到这个:
Compiled from "Tmp.java"
public class Tmp extends java.lang.Object{
public Tmp();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: iconst_1
10: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokeinterface #5, 2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
18: pop
19: return
}
Run Code Online (Sandbox Code Playgroud)
你会看到唯一的区别是第一个(使用an ArrayList
)进行调用invokevirtual
而另一个(使用List
用途)invokeinterface
. invokeinterface
实际上是一个慢于invokevirtual
(〜38%根据这个家伙)的头发.这显然是由于JVM在搜索具体类的虚方法表与接口的方法表时可以进行的优化.所以你说的其实是真的.接口调用是比具体的类调用速度较慢.
但是,你必须考虑我们正在谈论的速度.对于100,000,000次调用,实际差异为.03秒.所以你必须进行大量的调用才能真正显着降低速度.
另一方面,正如@ChinBoon指出的那样,编码到接口使得使用代码的人更容易,特别是如果你的代码返回某种类型List
.因此,在绝大多数情况下,编码的容易程度远远超过相对性能费用.
在阅读了@ MattQuigley的评论并在驱动器回家后对其进行了思考后添加了
这一切意味着这不是你应该担心的太多.任何表现增益或惩罚都可能非常小.
请记住,使用返回类型的接口和方法参数是一个非常好的主意.这允许您和使用您的代码的任何人使用List
最适合他们需求的任何实现.我的例子还表明,如果你碰巧使用一种返回的方法List
,99%的时间,你最好不要将它转换为具体的类,以获得性能提升.施法所需的时间可能超过表现的增益.
话虽这么说,这个例子也表明,对于一个局部变量,你确实最好使用一个具体的类而不是一个接口.如果你使用的唯一方法是on方法List
,那么你可以切换出没有副作用的实现类.此外,如果需要,您可以访问特定于实现的方法.此外,还有一个轻微的性能提升.
TL;博士
始终在方法上使用返回类型和参数的接口.对局部变量使用具体类是个好主意.它提供了较小的性能提升,如果您使用的唯一方法是在接口上,则切换实现没有成本.最后,你不要太担心它.(除了返回类型和参数之外.这很重要.)
aio*_*obe 12
"编程到接口"的最佳实践是否适用于局部变量?
尽管这种变量的范围比一个成员变量的范围较小,大部分的 利益编码对接口,当涉及到成员变量也适用于本地变量.
除非您需要特定于ArrayList的方法,否则请转到List.
我觉得性能命中率可以忽略不计,但可以肯定的是,使用ArrayList的List语义并没有太大的收获
我从未听说过调用接口方法比调用类类型的方法要慢.
使用ArrayList
而不是List
静态类型听起来像是对我的过早优化.像往常一样,在您对程序进行整体分析之前,优先考虑可读性和可维护性.
TLDR:方法调用是在接口/抽象类慢,几乎没有任何障碍,在稍后的时间点,这使得大多数参数的模拟点改变局部变量的实现.
这里有很多答案盲目地说将局部变量声明为接口而不理解为什么.首先,该方法调用是慢-约3倍,因为我在测试中召回.第add
一个代码块中的方法是第二个代码块的3倍.您可以通过一个测量for循环中经过时间的简单测试来自行测试.
List<Integer> numbers = new ArrayList<Integer>();
numbers.add(1); // slower
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(1); // faster
Run Code Online (Sandbox Code Playgroud)
其次,问题是关于局部变量,而不是一般界面的面向对象设计原则.有一个好的OO原因,公共类方法将返回接口或抽象类(List)而不是实现(ArrayList).你可以在其他地方看到这些原因,但这个问题与此无关 - 它是关于局部变量的.99.999%的时间你永远不会将该局部变量的实现从ArrayList
a 更改为a Vector
.但是,在实际执行的.001%的情况下,这样做没有任何障碍 - 您只需将单词Vector复制并粘贴到单词ArrayList上即可.每个人似乎都认为这种改变很难.不是.
第三,a LinkedList
与a 不是一回事ArrayList
.您选择一个或另一个是有原因的(访问/追加/插入/删除所需的时间).将其声明为ArrayList
是一种肯定,可以让您知道此局部变量用于快速随机访问.它是过早优化的论点是愚蠢的 - 只要它被实例化,它就会以某种方式进行优化.