Java泛型的类型擦除是否会导致完全类型转换?
我知道,当Java编译器编译泛型类型时,它会执行类型擦除,并从代码中删除对泛型的所有引用,而我的Java泛型的类型擦除是否会导致完全类型转换?,java,performance,generics,Java,Performance,Generics,我知道,当Java编译器编译泛型类型时,它会执行类型擦除,并从代码中删除对泛型的所有引用,而我的ArrayList就变成了ArrayList 我没有明确答案的问题是,缺少类型(因此强制类型转换)是否会导致速度减慢。换句话说,如果我使用的是标准Java编译器和Oracle的标准JVM 1.7: 字节码是否包含类型转换 如果是,是否包括运行时检查以检查其类型是否正确 转换本身需要花费大量的时间吗 我会制作自己的CheesecakeList类,该类看起来与ArrayList完全相同,只要所有对象都转
ArrayList
就变成了ArrayList
我没有明确答案的问题是,缺少类型(因此强制类型转换)是否会导致速度减慢。换句话说,如果我使用的是标准Java编译器和Oracle的标准JVM 1.7:
- 字节码是否包含类型转换
- 如果是,是否包括运行时检查以检查其类型是否正确
- 转换本身需要花费大量的时间吗
- 我会制作自己的
类,该类看起来与CheesecakeList
完全相同,只要所有ArrayList
对象都转到
我会得到什么(同样,这只是一个假设,我对拥有更大的二进制文件的任何副作用都不感兴趣,因为我的代码中有更多的类,也没有重复)Cheesecake
List<Cheesecake> list = new ArrayList<Cheesecake>();
for (int i = 0; i < list.size(); i++)
{
// I know this gets converted to ((Cheesecake)list.get(i)).at(me)
list.get(i).eat(me);
}
List List=new ArrayList();
对于(int i=0;i
与以下内容相比,循环内的转换是否昂贵和/或重要:
CheesecakeList list = new CheesecakeList();
for (int i = 0; i < list.size(); i++)
{
//where the return of list.get() is a Cheesecake, therefore there is no casting.
list.get(i).eat(me);
}
CheesecakeList=new CheesecakeList();
对于(int i=0;i
免责声明:这主要是一个学术好奇的问题。我真的怀疑类型转换是否存在任何重大的性能问题,如果我在代码中发现性能缺陷,消除对它们的需求甚至不是我要做的前五件事之一。如果你读到这篇文章是因为你确实存在性能问题,那你自己做吧帮个忙,启动一个分析器,找出瓶颈在哪里。如果你真的相信类型转换,那么你才应该尝试以某种方式优化它。类型检查在编译时完成。如果你这样做:
List<Cheesecake> list = new ArrayList<Cheesecake>();
这与任何其他向上转换(例如,
Object o=new Integer(5);
)没有什么不同。短篇故事:是的,有一个类型检查。这是证明-
鉴于以下类别:
// Let's define a generic class.
public class Cell<T> {
public void set(T t) { this.t = t; }
public T get() { return t; }
private T t;
}
public class A {
static Cell<String> cell = new Cell<String>(); // Instantiate it.
public static void main(String[] args) {
// Now, let's use it.
cell.set("a");
String s = cell.get();
System.out.println(s);
}
}
正如您可以在偏移量14
中所做的那样,对cell.get()
的结果进行类型检查,以验证它确实是一个字符串:
14: checkcast #34 // class java/lang/String
这确实会带来一些运行时的损失,但是,由于JVM已经进行了很好的优化,所以这种影响可能很小
长话短说:
您将如何实现这样一个CheesecakeList
类?该类不定义一个数组来保存元素吗?请注意,对数组的每个赋值都会导致一个隐藏的类型检查。因此,您不会获得您认为的那么多(尽管您的程序执行的读操作可能比写操作多,因此Cheesecak[]
数组将为您提供一些帮助)
底线:不要过早地优化
最后一点意见。人们通常认为类型擦除意味着Cell
被编译成Cell
。事实并非如此。擦除只适用于泛型类/方法的定义。它不适用于这些类/方法的使用站点
换句话说,类单元格
被编译为单元格
。如果T
有一个上限(比如Number
),则它被编译为单元格
。在代码的其他地方,通常是变量/参数,其类型是单元格
类的实例(例如Cell myCell
),不应用擦除。类文件中保留myCell
类型为Cell
的事实。这允许编译器正确地对程序进行类型检查
字节码是否包含类型转换
是的。中简要介绍了类型擦除
如果是,是否包括运行时检查以检查其类型是否正确
不可以。只要没有任何警告,就可以在编译时保证类型安全。
但是,也有一些桥接方法,如上面教程中所述
[编辑:澄清“否”意味着编译器不会因为使用泛型而插入额外的运行时检查。在强制转换期间仍会执行任何运行时检查]
转换本身需要花费大量的时间吗
你说的转换是指铸造吗?如果是,那么即使你没有泛型,情况也是如此
我会制作自己的CheesecakeList
类,该类看起来与ArrayList
相同
一般来说,这不是一个简单的问题,当然也不能不考虑其他因素。首先,我们不再谈论泛型。你要问的是,一个专门的类是否会比一个ArrayList
更快。假设是的,拥有一个避免强制转换的特殊类应该更快。但是真正的问题是你是否应该关心它
要回答最后一个问题,你必须具体。每种情况都是不同的。例如,我们谈论的对象有多少?数百万?重复调用?额外的时间在程序的性能中是否明显?你是否计时?我们希望我们的数据在未来如何扩展?等等
另外,不要忘记编译器是不断发展的。未来的版本可能会有一个自动修复性能问题的优化(如果你能接受的话),而如果你坚持使用一个无用的代码库,它可能会继续使用
public static void main(java.lang.String[]);
Code:
0: getstatic #20 // Field cell:Lp2/Cell;
3: ldc #22 // String a
5: invokevirtual #24 // Method p2/Cell.set:(Ljava/lang/Object;)V
8: getstatic #20 // Field cell:Lp2/Cell;
11: invokevirtual #30 // Method p2/Cell.get:()Ljava/lang/Object;
14: checkcast #34 // class java/lang/String
17: astore_1
18: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream;
21: aload_1
22: invokevirtual #42 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
25: return
}
14: checkcast #34 // class java/lang/String