Java泛型的类型擦除是否会导致完全类型转换?

Java泛型的类型擦除是否会导致完全类型转换?,java,performance,generics,Java,Performance,Generics,我知道,当Java编译器编译泛型类型时,它会执行类型擦除,并从代码中删除对泛型的所有引用,而我的ArrayList就变成了ArrayList 我没有明确答案的问题是,缺少类型(因此强制类型转换)是否会导致速度减慢。换句话说,如果我使用的是标准Java编译器和Oracle的标准JVM 1.7: 字节码是否包含类型转换 如果是,是否包括运行时检查以检查其类型是否正确 转换本身需要花费大量的时间吗 我会制作自己的CheesecakeList类,该类看起来与ArrayList完全相同,只要所有对象都转

我知道,当Java编译器编译泛型类型时,它会执行类型擦除,并从代码中删除对泛型的所有引用,而我的
ArrayList
就变成了
ArrayList

我没有明确答案的问题是,缺少类型(因此强制类型转换)是否会导致速度减慢。换句话说,如果我使用的是标准Java编译器和Oracle的标准JVM 1.7:

  • 字节码是否包含类型转换
  • 如果是,是否包括运行时检查以检查其类型是否正确
  • 转换本身需要花费大量的时间吗
  • 我会制作自己的
    CheesecakeList
    类,该类看起来与
    ArrayList
    完全相同,只要所有
    对象都转到
    Cheesecake
    我会得到什么(同样,这只是一个假设,我对拥有更大的二进制文件的任何副作用都不感兴趣,因为我的代码中有更多的类,也没有重复)
我对类型擦除引起的任何其他问题都不感兴趣,只是一个比我更了解JVM的人的简单回答

我对本例中的成本最感兴趣:

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